Skip to main content

Changements incompatibles avec les versions précédentes

Modifications des requêtes, des fonctions et de la syntaxe

  • Pour les vues dont le corps est un simple SELECT sur une seule table Distributed, l’intégralité de la requête externe est désormais transmise aux shards (nouveau paramètre optimize_trivial_view_pushdown_to_distributed, activé par défaut). Cela modifie le comportement observable de ces vues : les clauses FINAL et SAMPLE appliquées à la référence de la vue sont désormais propagées à la table locale du shard au lieu d’être ignorées, et extremes n’est plus renvoyé sur les clusters à un seul shard. Définissez optimize_trivial_view_pushdown_to_distributed = 0 pour rétablir le comportement précédent. #101791 (simonmichal).
  • Les patch parts des UPDATE légers utilisent désormais un nouveau format sur disque v2, trié par (sorting_key..., _block_number, _block_offset) et appliqué au moyen d’un nouvel algorithme de fusion. Le pic de mémoire est désormais borné par la plus longue séquence de clés de tri identiques, et non plus par la taille totale du patch, et les mises à jour qui franchissent les limites de fusion ne se rabattent plus sur une application par Join en mémoire. Les patch parts à l’ancien format restent lisibles. Lors d’une mise à niveau progressive depuis une version antérieure à 26.8, conservez patch_parts_version = 'v1' ou utilisez le paramètre compatibility jusqu’à ce que toutes les répliques soient mises à niveau. #103182 (CurtizJ).
  • AggregatingMergeTree rejette désormais, dès la création de la table, les schémas dans lesquels une colonne ne fait partie ni de la clé de tri ni d’une mesure d’état d’agrégation (AggregateFunction/SimpleAggregateFunction). Ces colonnes sont en effet réduites silencieusement à une valeur arbitraire lors des fusions en arrière-plan, ce qui produit des résultats erronés pour les requêtes qui effectuent un GROUP BY ou un filtrage sur celles-ci. Définissez allow_dimensions_outside_sorting_key = 1 pour rétablir le comportement précédent. #108087 (alexey-milovidov).
  • ClickHouse utilise désormais systématiquement le hachage unifié de déduplication des insertions, pour les insertions synchrones comme asynchrones ; les anciens comportements de déduplication par insertion sont supprimés. Le paramètre serveur insert_deduplication_version est conservé comme garde-fou de migration : le serveur refuse de démarrer s’il est défini sur une valeur legacy (old_separate_hashes ou compatible_double_hashes). Pour effectuer une mise à niveau depuis une version utilisant une valeur legacy, exécutez d’abord une version prenant en charge compatible_double_hashes (qui écrit à la fois les hachages legacy et unifiés). Pour les tables répliquées, exécutez-la pendant au moins replicated_deduplication_window_seconds (une heure par défaut ; avec les fenêtres par défaut, les hachages unifiés de toutes les insertions sont conservés sur cette période, ce qui est jugé suffisant pour une boucle de nouvelles tentatives d’insertion). Pour les tables non répliquées avec non_replicated_deduplication_window > 0, cette fenêtre repose sur un nombre d’insertions plutôt que sur une durée : exécutez donc compatible_double_hashes pendant au moins ce nombre d’insertions. Supprimez ensuite le paramètre (ou définissez-le sur new_unified_hash) avant de passer à cette version. #108361 (CheSema).
  • Le paramètre use_legacy_to_time vaut désormais 0 par défaut : toTime convertit donc les valeurs vers le type de données Time au lieu de convertir une date avec heure en une date fixe. L’ancien comportement reste disponible via la fonction toTimeWithFixedDate ou en définissant use_legacy_to_time = 1. #108729 (alexey-milovidov).
  • Les modèles Naive Bayes (utilisés par naiveBayesClassifier) sont désormais configurés sous forme de dictionnaire avec la disposition NAIVE_BAYES, construit au chargement à partir d’une table de comptages de n-grammes pré-agrégés par classe. Ils remplacent l’ancienne configuration côté serveur (un fichier de configuration XML référençant des fichiers de modèle .bin sérialisés), qui n’est plus prise en charge : les modèles existants doivent être recréés sous forme de dictionnaires. Trois nouvelles fonctions viennent compléter naiveBayesClassifier : naiveBayesClassifierWithProb renvoie la classe prédite accompagnée de sa probabilité, naiveBayesClassifierWithAllProbs renvoie chaque classe avec sa probabilité, et naiveBayesNgrams découpe le texte en n-grammes de la même manière que le dictionnaire, afin de construire les données d’entraînement à partir de texte brut étiqueté. Par ailleurs, plusieurs optimisations de performances ont été apportées : sur un modèle de langue à trigrammes de points de code de 9,7 MiB, la consommation mémoire est environ 49 fois plus faible (de 1,84 GiB à 38 MiB), et le chargement comme la classification sont environ 11 fois plus rapides. #108773 (nihalzp).
  • La valeur par défaut du paramètre max_insert_threads passe de 1 (aucun parallélisme) à auto, qui correspond au nombre de cœurs CPU disponibles pour le serveur (réduit en cas de pression mémoire via max_insert_threads_min_free_memory_per_thread). INSERT SELECT est ainsi parallélisé par défaut, et la partie écriture d’un INSERT simple éligible peut également l’être lorsque le chemin d’écriture de destination peut être réparti en toute sécurité. Cela peut modifier le nombre de parts créées et l’ordre des lignes insérées. Pour rétablir le comportement précédent, définissez max_insert_threads sur 1, ou compatibility sur une version antérieure à 26.8. #109000 (alexey-milovidov). #109006 (alexey-milovidov).
  • La lecture au format CSV d’un Tuple utilisé seul prend désormais un champ par élément à chaque position : un \N dans le champ d’un élément de premier niveau correspond donc à cet élément, et input_format_null_as_default s’y applique. Auparavant, un \N initial était interprété comme une valeur nulle pour la colonne entière : la ligne était alors remplacée par la valeur par défaut de la colonne, puis l’analyse des cellules restantes échouait. Ce changement est non rétrocompatible pour une ligne qui fournit un seul champ pour l’ensemble d’un Tuple utilisé seul : il lui manque désormais les éléments restants et elle est rejetée ; pour un Tuple à un seul élément, où le nombre de champs est identique dans les deux cas, elle produit silencieusement la valeur par défaut de l’élément au lieu du DEFAULT de la colonne. Définissez input_format_csv_deserialize_separate_columns_into_tuple = 0 pour lire de nouveau un tel champ comme la colonne entière. #109744 (groeneai).
  • Ajout de la clause VALID FOR <interval> à CREATE USER et ALTER USER, comme raccourci de VALID UNTIL. L’échéance d’expiration est calculée en ajoutant l’intervalle indiqué à l’heure courante au moment de l’exécution de la requête, puis stockée sous la forme VALID UNTIL. La colonne valid_until de la table system.users est désormais de type Array(DateTime64(0)) au lieu de Array(DateTime), afin de représenter exactement les échéances postérieures à l’année 2106 ; les outils qui lisent cette colonne doivent prendre en charge le nouveau type. #110171 (alexey-milovidov).
  • EXPLAIN SYNTAX renvoie désormais la requête reformatée sous la forme d’un unique enregistrement String (contenant des sauts de ligne) au lieu d’un enregistrement par ligne : sa sortie est ainsi une ligne unique, directement exploitable (par exemple, SELECT count() FROM (EXPLAIN SYNTAX ...) renvoie 1). Ce comportement est contrôlé par la nouvelle option single_record, dont la valeur par défaut est 1 ; définissez single_record = 0 pour rétablir l’ancienne sortie à un enregistrement par ligne. Les autres types d’EXPLAIN (PLAN/PIPELINE/AST) conservent leur sortie arborescente ligne par ligne. #110479 (alexey-milovidov).
  • La fonction hasColumnInTable n’accepte plus les arguments facultatifs hostname, username et password qui permettaient de vérifier une colonne sur un serveur distant arbitraire ; seule la forme hasColumnInTable(database, table, column) subsiste. Le mode distant supprimé posait un problème de sécurité : il permettait à n’importe quel utilisateur de déclencher des connexions sortantes vers des hôtes arbitraires, exposait des identifiants dans les journaux de requêtes et n’était soumis à aucun privilège. #110881 (alexey-milovidov).
  • Les moteurs de base de données PostgreSQL et MaterializedPostgreSQL respectent désormais la configuration remote_url_allow_hosts du serveur, à l’instar du moteur de table PostgreSQL, de la fonction de table postgresql et des dictionnaires créés par DDL. Cette politique s’applique lors d’un CREATE DATABASE et d’un ATTACH DATABASE exécuté par un utilisateur : si remote_url_allow_hosts est configuré sur votre serveur, la création ou le rattachement d’une base de données PostgreSQL ou MaterializedPostgreSQL pointant vers un hôte absent de la liste autorisée est désormais rejeté avec UNACCEPTABLE_URL. Les bases de données existantes continuent d’être chargées au démarrage du serveur, si bien qu’une mise à niveau ne peut pas empêcher le serveur de démarrer. #111135 (alexey-milovidov).
  • Suppression du lecteur et du rédacteur reposant sur la bibliothèque Apache Arrow pour les formats Arrow et ArrowStream. L’implémentation native de ClickHouse, utilisée par défaut depuis la version 26.7, est désormais la seule disponible. Les paramètres input_format_arrow_use_native_reader et output_format_arrow_use_native_writer sont obsolètes : ils sont toujours acceptés mais n’ont aucun effet, si bien qu’une requête qui les définissait à 0 pour forcer l’implémentation Apache Arrow utilise désormais l’implémentation native. #111996 (alexey-milovidov).
  • Les identifiants TLS d’une source MySQL (ssl_ca, ssl_cert, ssl_key) ne peuvent plus être spécifiés sous forme de chemins de fichiers depuis SQL — dans une collection nommée créée avec CREATE NAMED COLLECTION, comme argument de requête ou dans une requête CREATE DICTIONARY — car le serveur ouvre ces fichiers avec ses propres privilèges. Les chemins restent pris en charge dans le fichier de configuration du serveur. Dans les autres cas, transmettez le contenu du certificat ou de la clé dans les nouveaux paramètres ssl_ca_pem, ssl_cert_pem et ssl_key_pem, qui sont masqués dans les journaux et dans les requêtes SHOW, au même titre que les mots de passe. #112070 (alexey-milovidov).
  • disable_insertion_and_mutation empêche désormais la consommation en arrière-plan depuis les tables Kafka, RabbitMQ et NATS, tout en autorisant les écritures directes vers le stockage externe. Les tables Kafka2, NATS et RabbitMQ soumises à cette restriction n’initialisent pas de consommateurs pour les SELECT directs. Le paramètre message_queue_disable_insertion, introduit récemment, nécessite désormais un redémarrage du serveur. #113660 (cwurm).
  • Un PARTITION BY ou ORDER BY de fenêtre sur une colonne AggregateFunction est désormais rejeté avec ILLEGAL_COLUMN, comme l’était déjà un ORDER BY de premier niveau sur une telle colonne. Auparavant, une telle requête était acceptée par au moins un analyseur, et le PARTITION BY de fenêtre partitionnait différemment selon la valeur de max_threads. Ce refus s’applique également à un état imbriqué dans Array, Tuple, Map, Variant ou SimpleAggregateFunction. Un SimpleAggregateFunction sur un type ordinaire, QBit, ainsi que GROUP BY et DISTINCT sur un état, ne sont pas concernés. #113878 (groeneai).
  • Correction des contrôles d’accès pour les commandes SYSTEM ... CACHE ON CLUSTER. Elles exigeaient le groupe de privilèges SYSTEM DROP CACHE au lieu du privilège propre à chaque commande, ce qui, d’une part, refusait l’accès aux détenteurs d’un seul privilège de cache granulaire et, d’autre part, permettait à un détenteur du groupe d’exécuter SYSTEM SYNC FILESYSTEM CACHE ON CLUSTER sans disposer de SYSTEM SYNC FILESYSTEM CACHE. #114042 (groeneai).
  • Le table engine NATS accepte désormais des identifiants intégrés via le nouveau paramètre nats_credentials (même charge utile qu’un fichier .creds) et n’accepte plus nats_credential_file depuis SQL : ce path désigne un fichier situé sur le système de fichiers du serveur, que celui-ci ouvre avec ses propres privilèges. Il ne peut donc être spécifié que dans une collection nommée définie dans le fichier de configuration du serveur, ou via nats.credential_file directement dans la configuration du serveur. Une requête peut remplacer un tel path configuré par des nats_credentials intégrés, sauf si l’administrateur l’a verrouillé avec <nats_credential_file overridable="false">. Les tables créées avant cette restriction continuent de fonctionner après une mise à niveau. #114644 (alexey-milovidov).

Modifications des types de données et des formats

  • Rejet du format d’archive de sauvegarde zip/zipx pour les sauvegardes stockées sur un stockage d’objet — destinations directes S3(...)/AzureBlobStorage(...) et destinations Disk(...) reposant sur S3 ou Azure — pour BACKUP comme pour RESTORE. Le format Zip impose des accès aléatoires (seek) pour lire son répertoire central, ce qui est très lent sur un stockage d’objet. Utilisez plutôt un format basé sur tar, tel que tar.gz. #101770 (Onyx2406).
  • Extension de la plage prise en charge par DateTime64, qui passe de [1900-01-01, 2299-12-31] à [0000-01-01, 9999-12-31]. Les valeurs situées hors de l’ancienne plage sont désormais calculées correctement (via cctz) au lieu d’être ramenées à la borne. Avec une precision de 8 ou 9, la plage reste plus étroite, car les ticks sont stockés en Int64 (avec une précision à la nanoseconde, le maximum reste 2262-04-11). Rétrocompatibilité : si vous utilisiez auparavant des dates-heures hors plage en vous appuyant sur les règles de saturation très spécifiques de l’ancienne plage, sachez que les résultats changent désormais et sont plus exacts. #107907 (alexey-milovidov).
  • Un nombre JSON sans guillemets destiné à une colonne DateTime64/DateTime dans JSONEachRow et les formats similaires est désormais interprété comme un Unix timestamp (secondes écoulées depuis l’epoch), avec une précision inférieure à la seconde facultative, de manière cohérente avec le format Values, CAST et toDateTime64. Auparavant, un nombre comportant une partie fractionnaire (par ex. 1703363853.035) était rejeté avec l’erreur CANNOT_PARSE_INPUT_ASSERTION_FAILED, et un integer seul (par ex. 1703363853) était lu comme la valeur brute mise à l’échelle d’un DateTime64, ce qui produisait un timestamp en 1970-.... Il s’agit d’un changement non rétrocompatible pour les integers sans guillemets fournis à des colonnes DateTime64 ; les strings entre guillemets et la sortie JSON de ClickHouse lui-même (toujours entre guillemets) ne sont pas concernées. #108091 (alexey-milovidov).
  • Le codec expérimental ALP effectue désormais en Float64 les calculs de mise à l’échelle de Float32, ce qui élimine les exceptions sur les données décimales et améliore considérablement les taux de compression. #111627 (rienath).
  • Rejet des valeurs de paramètres de durée (millisecondes/secondes) qui dépassent la capacité d’un Int64 exprimé en microsecondes #112757 (azat).
  • Les métriques asynchrones peuvent désormais être de type Map, et les métriques par cœur de CPU et par périphérique ont été regroupées en métriques clé-valeur uniques : OSUserTimeCPU0, OSUserTimeCPU1, … sont devenues une seule métrique OSUserTimeCPU dotée d’une map associant le numéro du cœur de CPU à la valeur (il en va de même pour les autres métriques OS*TimeCPU*, CPUFrequencyMHz_*, Temperature*, EDAC*, Block*_*, Network(Receive|Send)*_*, Disk*_*, *BlobsQueueEstimate et AsyncLogging*QueueSize). system.asynchronous_metrics dispose d’une nouvelle colonne key_values Map(LowCardinality(String), Float64) (la colonne value vaut NaN pour ces métriques), system.asynchronous_metric_log les enregistre à raison d’une ligne par clé dans une nouvelle colonne key, le Prometheus endpoint les exporte avec un label (par ex. ClickHouseAsyncMetrics_BlockReadBytes{device="sda"}), et le MetricsTransmitter de Graphite les envoie sous la forme <prefix>.<Metric>.<key>. Si votre supervision s’appuie sur les anciens noms de métriques, définissez asynchronous_metrics_key_values_mode sur legacy_names pour les conserver, ou sur both pendant la migration ; ce paramètre serveur peut être appliqué avec SYSTEM RELOAD CONFIG, sans redémarrage. #115333 (alexey-milovidov). #116791 (alexey-milovidov).
  • Les disques de stockage objet utilisent désormais par défaut les transactions natives du stockage des métadonnées, ce qui renforce la cohérence des mises à jour de leurs métadonnées. #89658 (Michicosun).

Modifications des paramètres et de la configuration

  • Suppression des fonctions snowflakeToDateTime, snowflakeToDateTime64, dateTimeToSnowflake et dateTime64ToSnowflake, dépréciées de longue date. Elles avaient été dépréciées dès la v24.6 au profit de snowflakeIDToDateTime, snowflakeIDToDateTime64, dateTimeToSnowflakeID et dateTime64ToSnowflakeID, qu’il convient d’utiliser à la place. Le paramètre allow_deprecated_snowflake_conversion_functions (qui permettait de les réactiver) est désormais obsolète et n’a plus aucun effet. #108711 (alexey-milovidov).
  • Extension de la plage prise en charge par Date32, qui passe de [1900-01-01, 2299-12-31] à [0000-01-01, 9999-12-31], comme pour DateTime64. L’analyse et les conversions acceptent désormais la plage étendue au lieu de ramener silencieusement les valeurs aux anciennes bornes. Remarques sur la rétrocompatibilité : dans la conversion numérique toDate32(N), les valeurs comprises dans [120530, 2932896] sont désormais interprétées comme des numéros de jour (dates de 2300-01-01 à 9999-12-31) et non plus comme des timestamps Unix du début de l’année 1970, conformément à la règle selon laquelle tout nombre compris dans la plage des numéros de jour est traité comme un numéro de jour ; les nombres inférieurs au numéro de jour de 0000-01-01 et les timestamps postérieurs à 9999-12-31 sont ramenés aux nouvelles bornes. #111534 (alexey-milovidov).

Autres changements non rétrocompatibles

  • arrayIntersect et arraySymmetricDifference ne traitent plus une valeur répétée au sein d’un même argument comme si elle apparaissait dans plusieurs arguments. Les requêtes qui reposent sur l’ancien comportement peuvent désormais renvoyer des résultats différents : arrayIntersect([1], [2], [1, 1]) renvoie [] au lieu de [1], arrayIntersect([1, 2], [2], [1, 1, 2]) renvoie [2] au lieu de [1, 2], et arraySymmetricDifference([1], [2], [1, 1]) renvoie [2, 1] au lieu de [2]. Pour arraySymmetricDifference, deux arguments suffisent : arraySymmetricDifference([1], [2, 2]) renvoie [2, 1] au lieu de [1]. Désormais, une valeur n’est comptabilisée pour un argument que si elle figurait dans chacun des arguments précédents ; le résultat contient ainsi exactement les valeurs présentes dans tous les arguments. Dans le cadre de cette même modification, arrayIntersect construit sa table de hachage à partir du plus petit argument plutôt qu’à partir de tous, ce qui le rend jusqu’à 1,85 fois plus rapide et réduit d’un tiers sa consommation mémoire lorsque les arguments sont de tailles très différentes. arrayUnion n’est pas concerné. #113021 (alexey-milovidov).
  • Le type de statistiques de colonne minmax est désormais déprécié. #108680 (hanfei1991).
  • Correction de la vérification d’accès pour SYSTEM PREWARM PRIMARY INDEX CACHE ... ON CLUSTER, qui exigeait à tort le privilège SYSTEM PREWARM MARK CACHE au lieu de SYSTEM PREWARM PRIMARY INDEX CACHE. #109198 (Algunenano).
  • Correction de SYSTEM STOP/START CLEANUP et de SYSTEM STOP/START VIRTUAL PARTS UPDATE ON CLUSTER, qui exigeaient SYSTEM PULLING REPLICATION LOG au lieu de SYSTEM CLEANUP / SYSTEM VIRTUAL PARTS UPDATE. #110289 (groeneai).
  • Le glob / (nombre quelconque de répertoires) correspond désormais aussi au répertoire lui-même. Auparavant, / n’était pas traité comme un cas particulier dans les motifs glob : data/**/file.txt ne correspondait donc pas à data/file.txt (zéro niveau de répertoire). #97676 (alexey-milovidov).

Nouvelles fonctionnalités

Fonctions et types de données

  • Ajout des fonctions parseQueryToJSON et formatQueryFromJSON pour sérialiser et désérialiser des AST au format JSON, ainsi que d’un dialecte expérimental clickhouse_json activé par enable_json_ast_dialect. #100412 (alexey-milovidov). #113480 (fm4v).
  • Ajout de la table système system.stemmers, qui répertorie toutes les langues de racinisation disponibles pouvant être indiquées pour la fonction stem. #100611 (Ergus).
  • Ajout de la prise en charge des opérateurs postfixés AT TIME ZONE et AT LOCAL du SQL standard, en tant que sucre syntaxique pour toTimeZone. L’expression expr AT TIME ZONE zone est désormais équivalente à toTimeZone(expr, zone), et expr AT LOCAL à toTimeZone(expr, timeZone()). #106092 (lvzhipin03).
  • Le table engine URL et la fonction de table url redirigent désormais vers le backend approprié en fonction du schéma de l’URL : file:// est pris en charge par le moteur File, s3:///gs:///gcs:///oss:// par S3, az:///azure:///abfss:///abfs:// par AzureBlobStorage, hdfs:// par HDFS, et http(s):// par le moteur URL, comme auparavant. Le paramètre url_base est appliqué avant la redirection selon le schéma. Seuls les schémas S3 résolus par les url_scheme_mappers par défaut sont redirigés ; les autres schémas de fournisseurs compatibles S3 (cos, obs, …) ne le sont pas et nécessitent d’utiliser directement le moteur ou la fonction s3. #106093 (alexey-milovidov).
  • Ajout de la fonction dotProductTransposed (alias scalarProductTransposed), qui calcule le produit scalaire approximatif entre une colonne QBit et un vecteur de référence, en complément des fonctions existantes L2DistanceTransposed et cosineDistanceTransposed. #108100 (alexey-milovidov).
  • Ajout d’un paramètre facultatif de pas (stride) au type de données QBit (QBit(T, dimension, stride)), qui stocke des groupes de dimensions dans des flux distincts afin qu’une recherche vectorielle puisse lire efficacement les seules premières dimensions (par exemple pour les embeddings Matryoshka). Les fonctions de distance transposées acceptent un quatrième argument facultatif, used_dims, permettant de lire un nombre réduit de dimensions. #108103 (alexey-milovidov).
  • Ajout de la prise en charge du type d’élément Int8 dans le type de données QBit, ce qui permet le stockage et la recherche vectorielle par distance transposée (L2DistanceTransposed, cosineDistanceTransposed) sur des vecteurs quantifiés d’entiers 8 bits. #108105 (alexey-milovidov).
  • Nouvelle fonction randomHadamardTransform(vector[, seed[, output_dims]]) : une transformée de Hadamard randomisée déterministe d’un vecteur de flottants — une rotation orthogonale qui préserve la norme, utile pour prétraiter des embeddings avant quantification et (lorsqu’elle est tronquée) comme projection aléatoire de type Johnson–Lindenstrauss / Hadamard randomisée sous-échantillonnée. #108227 (alexey-milovidov).
  • Implémentation de la fonction d’agrégation mergedJSONPatch pour l’agrégation de type merge-patch (RFC 7396) sur des valeurs JSON, avec prise en charge de la fusion d’états et de l’utilisation avec AggregatingMergeTree. Il est possible d’agréger une colonne JSON en utilisant un horodatage ou une autre colonne d’ordonnancement comme clé de tri afin d’obtenir l’état le plus récent. #108349 (larryluogit).
  • Ajout de la fonction xxHash64Spark, qui calcule des valeurs xxHash64 compatibles avec Spark pour les entrées String et NULL en utilisant la graine 42, et renvoie un Int64. #108436 (lalitium).
  • Ajout de la fonction digits(n, offset[, length]), qui renvoie le nombre extrait de n commençant à l’index offset(1-indexed) et comportant length chiffres. Si length n’est pas fourni, la fonction renvoie le nombre depuis la position de départ jusqu’à la fin. #109012 (1000ms).
  • Ajout de la fonction arithmétique sqr, qui calcule le carré d’un nombre. #109061 (lalitium).
  • Ajout des fonctions de distance transposées quantifiées cosineDistanceTransposedQuantized, L2DistanceTransposedQuantized et dotProductTransposedQuantized, qui opèrent sur un QBit(Int8) de codes Lloyd-Max issus de quantizeBFloat16ToInt8, en déquantifiant à la volée les codes stockés. Un vecteur de référence à virgule flottante constitue la requête en pleine précision, comparée avec une précision Float32 (une requête Float64 est convertie à une précision inférieure) ; une référence Array(Int8) est elle-même déquantifiée, ce qui donne une distance symétrique entre valeurs quantifiées. #109405 (alexey-milovidov).
  • Ajout d’une nouvelle fonction dictGetRoot, qui renvoie l’ancêtre le plus élevé (la racine) d’une clé dans un dictionnaire hiérarchique. Elle offre un équivalent pratique à dictGetHierarchy(dict_name, key)[-1]. #109459 (alexey-milovidov).
  • Ajout de la fonction de table mergeTreeCodecBlockCounts(database, table), qui indique, pour chaque triplet (partie de données, colonne, sous-flux) d’une table MergeTree, le nombre de blocs compressés utilisant chaque codec. #109623 (rienath).
  • Ajout de la fonction notHas, négation de has pour les tableaux, les maps et le JSON. Lorsque l’ensemble de recherche est un tableau constant, notHas(constant_array, x) est réécrit en x NOT IN constant_array par optimize_rewrite_has_to_in (activé par défaut) : la fonction s’exécute alors via une recherche dans un set et peut élaguer les données grâce à l’index de clé primaire, comme NOT IN. #109926 (nihalzp).
  • Ajout du type de données géographique MultiPoint, stocké sous la forme Array(Point) et intégré au type Geometry. #109951 (davidmenggx).
  • Les fonctions arrayElement (l’opérateur vec[n]) et arraySlice prennent désormais en charge le type de données QBit : qbit[n] renvoie le n-ième élément du vecteur en pleine précision, et arraySlice(qbit, offset, length) renvoie une projection sur un sous-ensemble de dimensions. Toutes deux ne lisent que les plans de bits des groupes de stride nécessaires, et les tranches alignées sur les limites des groupes de stride réutilisent les flux stockés sans copie. #109953 (Utkal059).
  • Ajout d’un tokenizer splitByRegexp pour les index de texte et la fonction tokens. Il découpe l’entrée en jetons en utilisant une expression régulière comme séparateur, par exemple tokenizer = splitByRegexp('[^\p{L}\p{N}#+]+'), ce qui permet de conserver intacts les jetons contenant des caractères spéciaux tels que C++ ou C#, que d’autres tokenizers scinderaient. #110002 (Ergus).
  • Ajout des fonctions geometryIntersectCartesian et geometryIntersectSpherical, qui indiquent si deux géométries s’intersectent. Contrairement à polygonsIntersectCartesian/polygonsIntersectSpherical, elles acceptent n’importe quel type de données géométrique (Point, LineString, MultiLineString, Ring, Polygon, MultiPolygon), y compris le type générique Geometry, et les deux arguments peuvent être de types différents. #110062 (alexey-milovidov).
  • Le lecteur ORC natif prend désormais en charge le type uniontype d’ORC, qu’il fait correspondre au type ClickHouse Variant (auparavant, ces colonnes étaient rejetées avec l’erreur Unsupported ORC type). #110078 (alexey-milovidov).
  • Le format de sortie ORC natif peut désormais écrire le type ClickHouse Variant, qu’il fait correspondre à un uniontype ORC (auparavant, l’écriture échouait avec l’erreur ILLEGAL_COLUMN). #110085 (alexey-milovidov).
  • Ajout de formats de tramage, sélectionnés via le nouveau paramètre framing_output_format : ils multiplexent les différentes parties de la réponse à une requête dans un même flux de réponse HTTP — blocs de données, totaux et extrêmes, paquets de progression, événements de profilage, journaux du serveur et exceptions. Formats de tramage implémentés : None (par défaut, le comportement reste inchangé), EventStream (HTTP server-sent events), JSONEachPacketBase64 et JSONEachPacketString (un objet JSON par paquet, avec des données encodées en base64 ou sous forme de string). #110127 (alexey-milovidov).
  • Ajout de la fonction de table bigquery et du table engine BigQuery pour lire et écrire dans des tables Google BigQuery, y compris dans les jeux de données publics. La structure de la table est déduite du schéma de la table BigQuery. L’authentification prend en charge un jeton d’accès OAuth, une clé de compte de service au format JSON et un client OAuth avec jeton d’actualisation. #110166 (alexey-milovidov).
  • Nouvelle fonction aiRedact, qui détecte et masque les informations personnelles identifiables (PII) dans un texte à l’aide d’un fournisseur de LLM. Indiquez les catégories à masquer (par ex. ['email', 'name']) ou passez un tableau vide pour utiliser un ensemble de catégories de PII par défaut. Les valeurs détectées sont remplacées par un jeton ([REDACTED] par défaut). #110464 (davidmenggx).
  • Ajout de la fonction aiFilter, qui évalue une condition exprimée en langage naturel sur un texte à l’aide d’un LLM et renvoie un UInt8 utilisable dans WHERE, PREWHERE et JOIN ... ON. #110594 (ylw510).
  • Prise en charge des connexions TLS/SSL vers PostgreSQL pour le table engine PostgreSQL, la fonction de table postgresql, les moteurs de base de données PostgreSQL et MaterializedPostgreSQL, ainsi que les dictionnaires PostgreSQL : sslmode, plus les certificats et la clé, fournis soit sous forme de contenu littéral (sslrootcert_pem, sslcert_pem, sslkey_pem ; masqués comme les mots de passe), soit sous forme de chemins (sslrootcert, sslcert, sslkey ; acceptés uniquement depuis une collection nommée définie dans le fichier de configuration du serveur). #110615 (alexey-milovidov).
  • La fonction de table postgresql et le table engine PostgreSQL permettent désormais de se connecter à un autre serveur ClickHouse via le protocole PostgreSQL (lorsqu’un nom de table est utilisé ; la variante query(...) n’est pas encore prise en charge). Ajout des fonctions de compatibilité PostgreSQL format_type et current_setting. #110760 (alexey-milovidov).
  • Nouvelle fonction aiSimilarity qui calcule la similarité sémantique entre deux textes à l’aide d’un modèle d’embedding. Elle renvoie un Nullable(Float32) compris dans [-1, 1], où 1 signifie que les textes sont identiques, ou NULL si un opérande est NULL/vide ou si le calcul de son embedding a échoué. #110777 (davidmenggx).
  • Ajout des moteurs de base de données Remote et RemoteSecure, qui offrent un accès en temps réel aux tables d’une base de données située sur un serveur ClickHouse distant, en lui transmettant les requêtes SELECT et INSERT. Ils sont aux échanges entre serveurs ClickHouse ce que les moteurs de base de données MySQL et PostgreSQL sont à ces systèmes. #110975 (alexey-milovidov).
  • Prise en charge des opérateurs pipe dans les requêtes SQL : FROM t |> WHERE x > 1 |> AGGREGATE count() AS c GROUP BY y |> ORDER BY c DESC |> LIMIT 10, à l’image de la syntaxe pipe de GoogleSQL. Toute requête SELECT peut être suivie d’une chaîne d’opérateurs |>, chaque opérateur encapsulant la requête qui le précède dans une sous-requête ; l’AST obtenu est donc identique à celui de la requête équivalente écrite avec des sous-requêtes imbriquées. Dans une requête qui commence par la clause FROM, la clause SELECT est désormais facultative et vaut par défaut SELECT *. #111151 (alexey-milovidov).
  • Ajout de la fonction d’agrégation gini, qui calcule le coefficient de Gini de valeurs numériques finies et non négatives, y compris dans le cadre d’agrégations groupées et distribuées. #112280 (amirreza1307). #114643 (groeneai).
  • Prise en charge du PNG animé dans le format de sortie PNG. Une colonne t transforme le résultat d’une requête en animation ; l’échelle de temps de t est définie par output_format_image_time_multiplier_seconds et output_format_image_time_divisor_seconds, tandis que output_format_image_streaming_animation permet d’écrire les images au fur et à mesure que la requête les produit, au lieu de les mettre en mémoire tampon. #112846 (alexey-milovidov).
  • Ajout d’une colonne finish_time à system.mutations, qui enregistre la date de fin d’une mutation. Pour les mutations non terminées et celles dont la date de fin est inconnue, cette colonne vaut zéro. #113474 (nikitamikhaylov).
  • Ajout d’un nouveau type de schéma bucketed pour system.metric_log, qui stocke toutes les métriques dans une seule colonne Map(Enum16(...), Int64) à l’aide de la sérialisation Map par buckets (128 buckets), ainsi qu’une colonne ALIAS par métrique pour assurer la compatibilité : la table ne comporte plus que quelques colonnes au lieu de plusieurs milliers, les valeurs nulles ne sont pas stockées, et la lecture d’une seule métrique ne lit qu’un seul des 128 buckets. En outre, une Map avec des clés Enum peut désormais être indexée par le nom (string) de la valeur de l’enum, par exemple map['name']. #115380 (alexey-milovidov).
  • Ajout d’un tokenizer chinese pour la fonction tokens et les index de texte MergeTree. Il segmente le texte chinois en mots à l’aide d’un dictionnaire et d’un modèle de Markov caché (l’algorithme reprend celui de jieba), avec deux granularités : coarse_grained (par défaut) et fine_grained. #89945 (amosbird).
  • Ajout de la fonction d’agrégation groupFormat, qui met en forme les lignes de chaque groupe selon un format de sortie donné et renvoie le résultat sous forme de string. #93201 (wandersofb).
  • Ajout du combinateur de fonction d’agrégation -Tuple, qui applique indépendamment la fonction d’agrégation sous-jacente à chaque élément d’une colonne Tuple et renvoie un Tuple contenant les résultats, en conservant les noms des éléments : sumTuple(t) pour t = (a, b) renvoie (sum(a), sum(b)). Les fonctions d’agrégation à plusieurs arguments prennent un tuple par argument, les éléments étant appariés selon leur position : corrTuple((a1, a2), (b1, b2)) renvoie (corr(a1, b1), corr(a2, b2)). Contrairement à -ForEach appliqué aux tableaux, les éléments peuvent être de types différents, et le type de résultat ainsi que le nom de chaque élément sont conservés. #98190 (RinChanNOWWW).

Fonctionnalités SQL et de requêtes

  • Ajout de la prise en charge des clauses WHERE dans les définitions de projections. Les projections avec WHERE ne matérialisent que les lignes correspondant au prédicat, et l’optimiseur peut les utiliser (selon une estimation des coûts) lorsque la clause WHERE de la requête implique celle de la projection. #102347 (SBALAVIGNESH123).
  • Ajout d’un moyen d’accéder aux tables et aux bases de données via des chemins d’URL dans l’interface HTTP (par ex. `/database/table.format.gz?filter=a>0`), ainsi que de nouveaux paramètres (`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`) qui se combinent entre eux et avec le paramètre d’URL `query` existant. #105249 (alexey-milovidov).
  • Ajout des moteurs de table Remote et RemoteSecure, équivalents persistants des fonctions de table remote et remoteSecure. CREATE TABLE ... ENGINE = Remote('addresses', db, table, ...) fonctionne désormais en plus de CREATE TABLE ... AS remote(...). #106189 (alexey-milovidov).
  • Ajout de EXPLAIN ANALYZE pour analyser les performances des requêtes : la requête est exécutée et les métriques d’exécution réelles sont présentées dans le format habituel du plan de requête. #106586 (Fgrtue). #110668 (Fgrtue).
  • Ajout des commandes SYSTEM STOP, SYSTEM START, SYSTEM PAUSE, SYSTEM CANCEL et SYSTEM REFRESH, indépendantes du moteur, ainsi que de leurs variantes ... ALL BACKGROUND à l’échelle du serveur, pour contrôler l’activité en arrière-plan des tables Kafka, RabbitMQ, NATS, S3Queue/AzureQueue et des vues matérialisées actualisables via une interface unifiée. Pour les vues matérialisées actualisables, elles servent d’alias aux commandes SYSTEM ... VIEW existantes. Dans le cadre de la prise en charge de ces contrôles pour NATS, les tables JetStream n’acquittent désormais les messages qu’après une insertion réussie (sémantique « au moins une fois » ; auparavant, les messages étaient acquittés automatiquement à la livraison et pouvaient être perdus en cas d’échec de l’insertion). Un nouveau paramètre nats_wait_for_flush_interval (false par défaut, ce qui conserve le comportement précédent à faible latence) permet de maintenir un cycle de consommation ouvert pendant tout l’intervalle de vidage, au lieu de procéder au vidage dès que la file d’attente est vide. Un nouveau paramètre nats_commit_on_select fait en sorte qu’un SELECT direct sur une table JetStream consomme (acquitte) les messages qu’il lit. #107476 (NIKTONIKTO717).
  • Les fonctions de table et moteurs de table mysql, postgresql et sqlite acceptent désormais une requête utilisateur (au lieu d’un nom de table) et la transmettent telle quelle à la base de données externe, sous forme de sous-requête (SELECT ...) ou sous la forme query('SELECT ...'). La structure de la table obtenue est déduite du résultat de la requête, et une telle table est en lecture seule. #107740 (alexey-milovidov).
  • Introduction du moteur de table QueryRunner. Les enregistrements insérés dans une table QueryRunner représentent des requêtes que le moteur exécute. Ce moteur peut servir à l’exécution asynchrone de requêtes, à l’exécution par lots de requêtes générées, à l’acheminement de requêtes vers des clusters distants, aux benchmarks, au fuzzing et aux tests avec du trafic fantôme. #107888 (mstetsyuk).
  • Prise en charge du mode de cadre GROUPS pour les fonctions de fenêtre (SQL:2011), par exemple any(price) OVER (PARTITION BY symbol ORDER BY ts GROUPS BETWEEN CURRENT ROW AND 1 FOLLOWING). Dans un cadre GROUPS, les bornes se comptent en groupes de pairs entiers (ensembles de lignes ayant la même valeur pour la clé ORDER BY) : N PRECEDING/N FOLLOWING désignent donc N groupes de pairs avant/après le groupe de pairs de la ligne en cours, et non des lignes physiques (ROWS) ou des écarts de valeurs ORDER BY (RANGE). #108653 (nihalzp).
  • Une instruction CREATE MATERIALIZED VIEW ... POPULATE simple est désormais atomique en local : l’abonnement de la vue aux nouvelles insertions dans la table source et le snapshot des données existantes sont effectués ensemble sous un bref verrou exclusif sur la source, de sorte que les lignes insérées via le même serveur pendant le remplissage ne sont plus ni perdues ni dupliquées (nouveau paramètre materialized_views_populate_atomically, activé par défaut). Cette garantie ne couvre que le chemin d’insertion local (les insertions arrivant sur une autre réplique ou via un chemin d’écriture distribué en sont exclues) et elle nécessite une source capable de fournir un snapshot figé (famille MergeTree et Memory) ; les autres sources, ainsi que CREATE OR REPLACE / REPLACE et les vues créées dans des bases de données Replicated, conservent l’ancien remplissage non atomique. POPULATE peut désormais aussi être combiné avec TO pour remplir rétroactivement la table cible. #108715 (alexey-milovidov).
  • ALTER USER, ALTER ROLE et ALTER SETTINGS PROFILE acceptent désormais SET name = value comme alias de MODIFY SETTING name = value. Cette syntaxe modifie des paramètres individuels sur place sans toucher aux autres, contrairement à la clause SETTINGS seule, qui remplace l’intégralité de la liste des paramètres. Le risque d’effacer accidentellement les autres paramètres d’un utilisateur s’en trouve réduit. #108722 (groeneai).
  • Prise en charge de ALTER TABLE ... MODIFY CONSTRAINT [IF EXISTS] name CHECK expr pour modifier sur place l’expression d’une contrainte existante. #108768 (alexey-milovidov).
  • Ajout de l’algorithme IEJoin, fondé sur le tri, pour les jointures dont la section ON comporte deux comparaisons d’inégalité (<, <=, >, >=) entre les tables jointes ; il s’active en ajoutant ie_join au paramètre join_algorithm. Les types pris en charge sont ALL INNER/LEFT/RIGHT/FULL JOIN et SEMI/ANTILEFT/RIGHT JOIN. Auparavant, ces requêtes étaient exécutées sous forme de CROSS JOIN avec un filtre (INNER uniquement), ce qui est nettement plus lent sur de grandes tables. #109920 (vdimir).
  • Ajout d’une nouvelle table système, system.user_query_log, qui permet à chaque utilisateur de consulter ses propres enregistrements du journal de requêtes sans avoir besoin d’accéder à la table du journal de requêtes. L’implémentation d’origine et l’idée reviennent à Yue Ni (@niyue). #110156 (alexey-milovidov).
  • Nouveau paramètre run_query_in_background. Le serveur accepte la requête, renvoie immédiatement un résultat vide et l’exécute jusqu’au bout, quoi qu’il advienne de la connexion. Le résultat est ignoré. Suivez la requête grâce à son query_id dans system.processes et system.query_log. Destiné aux requêtes longues telles que INSERT ... SELECT, CREATE TABLE … AS SELECT ou CREATE MATERIALIZED VIEW … POPULATE, qui ne doivent pas être interrompues par la perte de la connexion. #112816 (mstetsyuk).
  • Prise en charge de ALTER TABLE … MODIFY PROJECTION pour modifier les paramètres propres à une projection existante (par exemple index_granularity) sans avoir à la reconstruire. Les nouveaux paramètres s’appliquent de manière différée, au fil des fusions. #113343 (diegomestre2).
  • Le catalogue du moteur S3 Tables pour les lacs de données prend désormais en charge INSERT, en plus de l’interrogation des tables existantes. #113505 (scanhex12).
  • Le catalogue Iceberg prend désormais en charge Snowflake Horizon Catalog via catalog_type = 'horizon', ce qui permet de lire et d’écrire des tables Iceberg gérées par Snowflake. #114547 (melvynator).
  • Ajout du paramètre skip_unavailable_shards_mode (également disponible en tant que paramètre du moteur Distributed), qui permet de choisir quelles exceptions provenant d’une partition (shard) distante sont ignorées silencieusement lorsque skip_unavailable_shards est activé. Il offre ainsi un contrôle plus fin du comportement des requêtes dans les environnements distribués. #79091 (byte-sourcerer).

Stockage, lacs de données et stockage d’objet

  • Ajout de la prise en charge du format de fichier Puffin (https://iceberg.apache.org/puffin-spec/). Ce format peut désormais être utilisé dans les fonctions de table telles que file, url, s3, etc. #103936 (scanhex12).
  • Ajout d’un mode de stockage de parties de données « packed » pour les tables MergeTree, qui regroupe l’ensemble des fichiers d’une partie de données dans une archive unique (data.packed) au lieu d’un fichier par flux. Ce mode est contrôlé par les paramètres min_bytes_for_full_part_storage, min_rows_for_full_part_storage et min_level_for_full_part_storage, et il est désactivé par défaut. Remarque sur la compatibilité : dès qu’une table écrit des parties de données packed, les versions antérieures du serveur ne peuvent plus les lire ; l’activation de ce format empêche donc tout retour à une version antérieure du serveur. #108118 (Algunenano).
  • Ajout du paramètre nats_credentials au table engine NATS, qui permet aux utilisateurs de spécifier directement les identifiants NATS sous forme de string (correspondant à la charge utile d’un fichier .creds). #110733 (addshore).

Formats de données et ingestion

  • Ajout du format de sortie HiveText, qui écrit les données au format texte LazySimpleSerDe d’Apache Hive. Les champs sont séparés par \x01, les lignes par le délimiteur défini dans le nouveau paramètre format_hive_text_rows_delimiter (\n par défaut), et les valeurs imbriquées de type Array, Map et Tuple utilisent la liste de séparateurs de Hive. Ce format de sortie cible le LazySimpleSerDe par défaut de Hive et n’est pas symétrique avec le format d’entrée HiveText de ClickHouse en ce qui concerne les valeurs imbriquées ou les délimiteurs de lignes personnalisés. #107582 (alexey-milovidov).
  • Ajout du format de sortie GeoJSON, qui permet d’écrire des documents GeoJSON FeatureCollection à raison d’une entité par ligne. Le nouveau paramètre format_geojson_validate_geometry (activé par défaut) valide les formes GeoJSON en lecture comme en écriture ; le format d’entrée GeoJSON infère désormais id comme Nullable(String), ce qui permet de distinguer les identifiants absents ou null des strings vides. #108065 (nihalzp).

Paramètres, accès et observability

  • Ajout d’une page intégrée de recherche dans la documentation, accessible via le path /docs de l’interface HTTP, qui permet une recherche instantanée dans la table system.documentation et affiche la documentation de référence (avec coloration syntaxique, formules mathématiques et liens croisés). #108345 (alexey-milovidov).
  • Ajout d’un tokenizer japonais pour les index de texte et les fonctions tokens/hasAnyTokens/hasAllTokens, reposant sur l’analyseur morphologique MeCab. Le dictionnaire est chargé à l’exécution depuis un emplacement défini dans la configuration du serveur, puis vérifié au moyen d’une somme de contrôle SHA-256 avant utilisation. #111420 (Ergus).
  • Ajout d’un nouveau paramètre du cache du système de fichiers, system_cache_extensions, qui détermine les fichiers stockés dans le cache système lorsque use_split_cache est activé. #111575 (kirillgarbar).
  • Ajout du paramètre always_fetch_mutated_part, qui permet à une réplique de télécharger les parties de données mutées depuis d’autres répliques au lieu d’exécuter les mutations localement. #113020 (rorylshanks).

Autres nouvelles fonctionnalités

  • Ajout de la prise en charge de WITH TIES pour les valeurs négatives de LIMIT. #100930 (nihalzp).
  • Ajout des paramètres de restauration restore_table_data, restore_access_entities et restore_functions, qui permettent de contrôler finement ce qui est restauré. Ils peuvent remplacer structure_only catégorie par catégorie — par exemple, structure_only=true, restore_access_entities=true restaure les définitions de tables et les entités d’accès, sans les données des tables. #102402 (fm4v).
  • Le chargement différé peut désormais être configuré dictionnaire par dictionnaire à l’aide de dictionary_lazy_load dans la définition du dictionnaire, ce qui prévaut sur le paramètre serveur global dictionaries_lazy_load. Certains dictionnaires peuvent ainsi être chargés de manière différée, tandis que d’autres sont chargés immédiatement. #108314 (mstetsyuk).
  • L’opérateur d’indice de tableau accepte désormais un tableau d’entiers comme index : arr[indexes] renvoie les éléments situés à chacune des positions indiquées, ce qui équivaut à arrayMap(i -> arr[i], indexes). #108371 (ucasfl).
  • Ajout des fonctions geoToUTM, UTMToGeo, geoToMGRS et MGRSToGeo pour convertir des coordonnées géographiques WGS84 vers les systèmes de coordonnées UTM et MGRS, et inversement. #108939 (alexey-milovidov).
  • Ajout des commandes SYSTEM UNLOAD DICTIONARY et SYSTEM UNLOAD DICTIONARIES pour libérer la mémoire occupée par les dictionnaires sans supprimer leur définition. Les dictionnaires seront rechargés de manière différée lors du prochain accès. #109639 (Manerone).
  • Ajout d’un nouveau tokenizer icu pour les index de texte et les fonctions tokens, hasAnyTokens et hasAllTokens, qui offre une segmentation Unicode en mots adaptée à la locale (y compris une segmentation à base de dictionnaire pour des langues comme le chinois, le japonais et le thaï). #109940 (Ergus).
  • Ajout d’un nouveau codec ZXC — un codec LZ asymétrique à compression lente et décompression très rapide, dont le taux de compression se situe entre ceux de LZ4 et de ZSTD. #110620 (alexey-milovidov).
  • Ajout du paramètre s3_base, qui permet de résoudre les URL relatives des fonctions de table s3, s3Cluster, gcs et oss ainsi que du table engine S3 par rapport à une URL de base S3. Ajout du moteur de base de données URL, qui résout les noms de tables en tant qu’URL par rapport à une base facultative et les transmet au backend approprié. #111512 (alexey-milovidov).
  • Prise en charge de l’expansion des caractères génériques pour url() et le moteur URL, par énumération des pages d’index HTTP (listes de répertoires au format HTML ou texte brut) et extraction des URL correspondantes. Les caractères génériques sont appliqués au composant de chemin de l’URL, avec des limites sur la taille des pages d’index et sur le nombre de répertoires parcourus. #95181 (niyue).
  • Ajout de AWS_MSK_IAM comme valeur prise en charge pour kafka_sasl_mechanism, ce qui permet à ClickHouse de s’authentifier auprès d’Amazon MSK à l’aide de rôles IAM, sans avoir à gérer d’identifiants SASL/SCRAM. #96100 (kalavt).
  • Ajout d’un postprocesseur à l’index de texte, qui transforme les jetons après la tokenisation. #98939 (Ergus).

Fonctionnalités expérimentales

Planification et exécution des requêtes

  • Ajout de “drivers” pour les fonctions exécutables définies par l’utilisateur. Un driver déclaré dans <user_defined_executable_function_drivers_config> peut être utilisé dans CREATE FUNCTION name ARGUMENTS (...) RETURNS T ENGINE = DriverName(...) AS '...code...' pour compiler ou traiter d’une autre manière un extrait de code utilisateur lors de la création de la fonction, et produire une UDF exécutable prête à l’emploi. La configuration obtenue est stockée dans <dynamic_user_defined_executable_functions_path>, et la requête d’origine est conservée sous la forme ATTACH FUNCTION afin que la fonction persiste après un redémarrage du serveur. Un driver de démonstration, c_function_body, compile et exécute des corps de fonctions C dans des conteneurs Docker isolés en mode executable_pool. #105131 (alexey-milovidov).
  • Les requêtes adressées à la Prometheus Query API sur /api/v1/query et /api/v1/query_range sont désormais enregistrées dans system.query_log avec les métriques read_rows et read_bytes. #106611 (JTCunning).
  • Prise en charge des lectures via un plan de requête distribué pour SELECT ... FINAL sur les tables de la famille MergeTree lorsque make_distributed_plan est activé. #108148 (davenger).
  • Ajout d’une exécution expérimentale des répliques parallèles basée sur le plan pour les requêtes MergeTree. #108504 (devcrafter).
  • Ajout d’une fonction de table expérimentale eval, qui évalue une expression constante sous forme de chaîne de requête, puis exécute l’unique requête SELECT obtenue. Cette fonctionnalité est désactivée par défaut et peut être activée via le paramètre allow_experimental_eval_table_function. Auteur : Yue Ni #110132 (alexey-milovidov).
  • Répliques parallèles basées sur le plan (parallel_replicas_plan_based) : la frontière entre exécution locale et distante est désormais déterminée par une optimisation au niveau du plan, appliquée à un plan de requête standard, et les requêtes sur des vues contenant un UNION sont parallélisées sans parallel_replicas_allow_view_over_mergetree. Les requêtes utilisant IN (subquery) basculent désormais vers un plan local au lieu d’échouer. #111063 (devcrafter). #112443 (devcrafter).
  • Renforcement de la sécurité des fonctions d’IA expérimentales : refus par défaut des endpoints non sécurisés (http) vers des hôtes distants (nouveau paramètre ai_function_allow_insecure_endpoint), plafonnement par défaut du nombre d’appels d’API sortants par requête (ai_function_max_api_calls_per_query vaut désormais 1000 par défaut) et assainissement des réponses d’erreur des fournisseurs écrites dans les journaux. #111286 (george-larionov).
  • Les répliques parallèles basées sur le plan (parallel_replicas_plan_based) distribuent désormais les JOIN éligibles (INNER, LEFT, RIGHT, y compris SEMI/ANTI) : la jointure est exécutée dans son intégralité au sein d’un fragment de plan distribué, l’un des côtés étant coordonné entre les répliques et l’autre diffusé. #112268 (devcrafter).
  • Signalement de l’erreur ayant provoqué l’annulation d’une requête distribuée (make_distributed_plan = 1) au lieu d’un simple QUERY_WAS_CANCELLED. Auparavant, l’erreur réelle d’une task de worker en échec n’apparaissait que dans le journal du serveur. #112590 (davenger).
  • Prise en charge de l’exécution distribuée des plans de requête contenant IN (subquery) sans réécriture en JOIN. Le set est construit une seule fois sur l’initiateur et ses valeurs sont transmises avec les tasks des workers. Le forçage de rewrite_in_to_join est supprimé ; la réécriture reste disponible via un paramètre explicite. #113826 (davenger).
  • Une requête à plan distribué (make_distributed_plan) arrête désormais ses étapes en amont dès qu’une clause LIMIT est satisfaite, au lieu de calculer le résultat complet pour ensuite l’écarter. #114806 (davenger).
  • Correction de l’erreur Illegal type Decimal(18, 0) of argument of function toIntervalNanosecond lorsqu’une requête PromQL utilise offset dans un sélecteur de plage, par exemple rate(m[2m] offset 5m). #114997 (nikitamikhaylov).
  • Prise en charge de l’agrégation GROUPING SETS dans les plans distribués construits par l’optimiseur de requêtes Cascades (make_distributed_plan = 1, enable_cascades_optimizer = 1). #115426 (davenger).
  • Les tables TimeSeries peuvent désormais conserver une table d’« échantillons récents » : lorsque le nouveau paramètre de moteur recent_samples_ttl_seconds est non nul, le moteur crée une copie de la table samples soumise à un TTL (partitionnée selon recent_samples_partition_by, par jour par défaut) et alimentée à chaque insertion ; les requêtes PromQL dont l’intervalle de temps s’inscrit dans la fenêtre TTL lisent alors automatiquement cette copie, ce qui accélère considérablement (de plusieurs fois) les règles d’alerte et d’enregistrement ainsi que les tableaux de bord sur de courtes plages, sur les tables volumineuses. Ce comportement peut être désactivé à l’aide du nouveau paramètre de niveau requête time_series_prefer_recent_samples_table. #115441 (nikitamikhaylov).
  • Les plans de requête distribués (make_distributed_plan) arrêtent désormais sans délai les étapes en amont inactives une fois une clause LIMIT satisfaite : un récepteur d’échange inactif lit NoMoreDataNeeded dès sa réception, et une source d’échange inactive détecte la fermeture de sa sortie et propage l’arrêt en amont. Auparavant, ces étapes continuaient à lire et à calculer des données qui n’étaient consommées par personne. #115690 (davenger).
  • Le mode automatique des répliques parallèles peut désormais collecter des statistiques d’exécution et activer les répliques parallèles pour les requêtes prises en charge lorsque parallel_replicas_plan_based est activé. #115788 (devcrafter).
  • Ajout de Cascades, un optimiseur de requêtes expérimental basé sur les coûts pour les plans de requête distribués, activé via enable_cascades_optimizer = 1 combiné à make_distributed_plan = 1. En fonction du coût estimé, il choisit entre les stratégies de jointure par shuffle, broadcast, répliquée et locale, l’agrégation en deux phases, par shuffle et locale, le top-N distribué en deux étapes, ainsi que les lectures parallèles et répliquées, en insérant des opérateurs d’échange si nécessaire. #86353 (davenger).

Stockage, lacs de données et index

  • Ajout d’un cache SLRU en mémoire pour les fichiers de métadonnées Paimon désérialisés (listes de manifestes et manifestes). Lorsque ce cache est activé via le paramètre use_paimon_metadata_files_cache, les requêtes répétées sur une même table Paimon n’ont plus besoin de retélécharger ni de réanalyser les métadonnées depuis le stockage d’objet. #104657 (Binnn-MX).
  • Le ReaderExecutor expérimental (use_reader_executor, désactivé par défaut) peut désormais maintenir ouverte une connexion à une source distante et la réutiliser d’une lecture séquentielle à l’autre, ce qui réduit le nombre de requêtes adressées au stockage d’objet lors des parcours. #107735 (CheSema).
  • Ajout d’un cache du système de fichiers en lecture directe (read-through) dans le chemin de lecture du ReaderExecutor expérimental (use_reader_executor, désactivé par défaut). #110029 (CheSema).
  • Ajout d’un paramètre MergeTree expérimental, allow_experimental_adaptive_codec_selection. Lorsqu’il est activé, les fusions et les mutations sélectionnent, bloc par bloc, le codec produisant la sortie la plus compacte pour les colonnes qui utilisent le codec par défaut. #111834 (rienath).
  • Ajout d’un niveau de cache de pages en espace utilisateur dans le chemin de lecture du ReaderExecutor expérimental (use_reader_executor, désactivé par défaut). En cas d’échec de cache (miss), l’executor alimente désormais le cache de pages en espace utilisateur, puis sert les lectures suivantes directement depuis celui-ci, sans copie (zero-copy). #114890 (CheSema).
  • La compaction des fichiers manifestes Iceberg est désormais disponible via OPTIMIZE TABLE ... MANIFEST, afin de réduire le nombre de fichiers de métadonnées. Elle est conditionnée par le paramètre allow_experimental_iceberg_compaction. #98178 (SmitaRKulkarni).

Fonctions, formats et IA

  • Expérimental : exécution de SELECT count(*) FROM t WHERE col <op> default(col) à partir des statistiques de parcimonie par colonne stockées dans serialization.json, sans aucun parcours des données, lorsque le prédicat partitionne exactement la colonne en valeurs par défaut et valeurs non par défaut. #105890 (Algunenano).
  • Ajout de la famille expérimentale de codecs vectoriels Quantize(...) ainsi que d’une réécriture facultative (opt-in) de la recherche vectorielle approximative en deux étapes sur des flux compagnons quantifiés. #108565 (shankar-iyer).
  • Réintroduction du codec expérimental SZ3 de compression avec perte à erreur bornée pour les colonnes Float32/Float64 (et les tableaux de ces types). Il nécessite allow_experimental_codecs. #108788 (alexey-milovidov).
  • Correction de la lecture des données compressées avec le codec expérimental SZ3('ALGO_LORENZO_REG', ...), qui échouait auparavant avec CORRUPTED_DATA, et élimination d’un comportement indéfini lors de la compression de valeurs à virgule flottante non finies (NaN et infinis) avec le codec expérimental SZ3. #110762 (alexey-milovidov).
  • PromQL : les fonctions de date/heure (hour(), minute(), month(), year(), day_of_week(), day_of_month(), day_of_year(), days_in_month()) peuvent désormais être appelées sans arguments ; elles utilisent alors vector(time()) par défaut, comme dans Prometheus. #111869 (valerypetrov).
  • PromQL : correction des opérations sur les vecteurs instantanés sans tags, par exemple vector(1) + vector(2), topk(1, vector(1)), label_replace(vector(1), ...) et les opérateurs binaires avec on(), qui échouaient avec une erreur de type. #111872 (valerypetrov).
  • PromQL : implémentation de la fonction deriv, avec correction d’un bug qui faisait renvoyer à la fonction d’agrégation sous-jacente des valeurs 1000 fois trop petites avec des timestamps à la milliseconde. #112360 (valerypetrov).
  • Application de l’optimisation de lecture dans l’ordre aux requêtes ORDER BY lorsque parallel_replicas_plan_based est activé : le tri est désormais transmis aux répliques puis fusionné sur l’initiateur, de sorte que chaque réplique lit les données dans l’ordre de la clé de tri. Corrige également l’erreur Unknown function __topKFilter pour ORDER BY sur une colonne ne faisant pas partie de la clé primaire lorsque ce paramètre est activé. #114315 (devcrafter).
  • Correction des lectures de recherche vectorielle après le rechargement d’une part (par exemple après ATTACH ou un redémarrage du serveur) : les parts ne perdent plus leurs sous-colonnes de codec Quantized, de sorte que les recherches lisent les sous-colonnes quantifiées au lieu de se rabattre sur la colonne en pleine précision. #114763 (shankar-iyer).
  • Le table engine expérimental TimeSeries n’applique plus le codec Gorilla aux colonnes value créées automatiquement dans la table interne des échantillons ; elles utilisent désormais CODEC(ZSTD(1)) par défaut. Les colonnes déclarées explicitement conservent les codecs définis par l’utilisateur. La valeur par défaut de la colonne timestamp reste inchangée (CODEC(DoubleDelta, ZSTD(1))). #112110 (nikitamikhaylov). #114790 (nikitamikhaylov).
  • Prise en charge de ROLLUP, CUBE, GROUPING SETS et de la fonction grouping dans les requêtes exécutées avec make_distributed_plan = 1. #114950 (davenger).
  • Ajout d’une variante Real Doubles (RD) et d’un argument facultatif ALP(AUTO|STD|RD) au codec expérimental ALP. Utilisé sans argument, ALP choisit désormais automatiquement entre le schéma STD existant et RD, au lieu d’utiliser systématiquement STD. #99654 (nazarii-piontko).

Autres fonctionnalités expérimentales

  • Ajout d’une affinité statique entre partitions et shards pour StorageKafka2 via les paramètres kafka_partition_shard_num et kafka_shard_count, permettant à plusieurs shards ClickHouse de consommer de manière déterministe des sous-ensembles disjoints de partitions Kafka. #108886 (JingYanchao).
  • Vous pouvez désormais vous authentifier auprès de OneLake à l’aide d’un jeton bearer obtenu au préalable, via le paramètre onelake_bearer_token, au lieu de onelake_client_id et onelake_client_secret. Cela évite de partager un secret client à longue durée de vie. Le jeton n’étant pas renouvelé, la base de données doit être recréée à son expiration. #109104 (asya-ch).
  • Correction de data_uncompressed_bytes pour les index de saut regroupés dans skp_idx.packed (via packed_skip_index_max_bytes) : la taille non compressée indiquée correspondait en réalité à la taille compressée, ce qui pouvait également empêcher l’activation de distributed_index_analysis. #109272 (Algunenano).
  • Ajout de la prise en charge de la compression Zstandard dans le gestionnaire HTTP expérimental Prometheus remote-write v1. #110907 (JTCunning).
  • PromQL : implémentation des fonctions clamp, clamp_min, clamp_max et round. #111870 (valerypetrov).
  • PromQL : quantile avec un niveau de quantile en dehors de [0, 1] renvoie désormais -Inf/+Inf/NaN, comme Prometheus, au lieu de lever une erreur, et histogram_quantile ignore désormais, comme Prometheus, les séries d’entrée dont le label le est absent ou non analysable. #111871 (valerypetrov).
  • PromQL : implémentation des fonctions changes() et resets(). #112352 (valerypetrov).
  • Les statistiques d’insertion et les requêtes passant par l’endpoint Prometheus remote-write sont désormais correctement collectées. #112825 (JTCunning).
  • PromQL : les valeurs finies sont désormais préservées lors d’un modulo avec un diviseur infini. #113746 (fallintoplace).
  • Rejet des sauts de ligne littéraux dans les strings PromQL ordinaires entre guillemets. #114381 (fallintoplace).
  • Une valeur très élevée de distributed_plan_workers_num ne provoque plus l’arrêt brutal du serveur avec une std::length_error lorsque les paramètres expérimentaux make_distributed_plan et enable_cascades_optimizer sont activés. Un tel nombre de nœuds est désormais rejeté avec INVALID_SETTING_VALUE. #115398 (groeneai).
  • Implémentation de l’endpoint /api/v1/series de la Prometheus HTTP API pour les tables TimeSeries : sélecteurs de séries match[] (les valeurs répétées sont combinées par union), intervalle de temps start/end facultatif et paramètre limit facultatif. #115676 (nikitamikhaylov).

Améliorations des performances

Performances des jointures et des agrégations

  • Activation du cache des conditions de requête pour les requêtes passant par le filtrage dynamique TopK (ORDER BY ... LIMIT n). L’entrée du QCC est partitionnée selon les paramètres du plan TopK : une même requête réutilise ainsi les résultats mis en cache des filtres WHERE, tandis qu’une valeur différente de LIMIT, de colonne de tri ou de sens de tri produit une nouvelle entrée. #104478 (alexey-milovidov).
  • Propagation de LIMIT dans l’agrégation dans l’ordre (aggregation-in-order) afin de permettre un arrêt anticipé lorsque la clé GROUP BY correspond à la clé ORDER BY et à la clé de tri de la table, ce qui réduit considérablement le nombre de lignes lues. #104859 (thevar1able).
  • Activation par défaut de allow_aggregate_partitions_independently. Lorsqu’une clé GROUP BY est compatible avec la clé de partition, ClickHouse peut agréger chaque partition indépendamment et se passer de l’étape de fusion globale. Des heuristiques d’exécution désactivent automatiquement l’optimisation lorsque l’organisation des partitions la rendrait défavorable (trop peu de partitions, trop de partitions ou tailles de partitions fortement déséquilibrées). #105128 (alexey-milovidov).
  • Activation par défaut de read_in_order_use_virtual_row. Lors d’une lecture dans l’ordre de la clé primaire (par ex. ORDER BY primary_key LIMIT n) sur une table comportant de nombreux parts, seuls les parts susceptibles de contribuer réellement au résultat sont lus (plus une fenêtre de lecture anticipée d’au plus max_threads parts, qui maintient la lecture en parallèle), ce qui réduit considérablement le pic de consommation mémoire. #106215 (alexey-milovidov).
  • Réduction de l’utilisation mémoire et amélioration des performances des JOIN. Le côté droit d’une jointure par hachage utilise désormais une référence de ligne compacte de 8 octets basée sur un index, de sorte que les entrées de la table de hachage des jointures ALL sont aussi petites que celles des jointures ANY, quel que soit le type de clé. Lors de bancs d’essai sur de grandes requêtes de jointure parallel_hash — jointures INNER et ANY de tables de 100 à 300 millions de lignes sur des clés UInt64 et String, avec des clés uniques comme dupliquées —, la requête médiane s’est révélée environ 12 % plus rapide, avec un pic de mémoire réduit d’environ 15 %, sans qu’aucune requête ne ralentisse. Ce sont les jointures INNER sur des clés UInt64 qui en ont le plus bénéficié (médiane environ 21 % plus rapide, avec 38 % de mémoire en moins). #107189 (harikrishnan94).
  • Ajout de l’algorithme d’énumération de l’ordre des jointures dpsub, qui fournit des plans de jointure optimaux avec un surcoût d’optimisation inférieur à celui de dpsize et prend en charge les jointures autres qu’internes. #107351 (fkastrati).
  • Les agrégations à un niveau peu gourmandes en mémoire ne sont plus converties en agrégations à deux niveaux : un memory tracker dédié est désormais rattaché à l’état d’agrégation, au lieu de s’appuyer sur la mémoire de l’ensemble de la requête. #107490 (m-selmi).
  • L’AST complet de la requête n’est plus construit lors de l’analyse des requêtes d’agrégation : la clé du cache des statistiques de taille de la table de hachage d’agrégation est désormais calculée à partir du plan de requête plutôt que de l’AST. #107643 (novikd).
  • Possibilité de basculer d’une jointure hash lors de la première exécution d’une requête vers parallel_hash lors des exécutions suivantes, en fonction des statistiques d’exécution collectées lors de la première exécution. #108125 (m-selmi).
  • Utilisation de la taille de la table de hachage issue des statistiques d’exécution des jointures pour optimiser la taille des runtime filters de jointure lors des exécutions ultérieures de la requête. Ce comportement est contrôlé par le nouveau paramètre join_runtime_filter_size_from_hash_table_stats. #108313 (m-selmi).
  • Accélération et réduction de l’utilisation mémoire des requêtes DISTINCT sur les tables MergeTree partitionnées, en conservant les lignes de chaque partition dans un seul flux : ainsi, le DISTINCT préliminaire de chaque flux porte sur un ensemble disjoint de clés, au lieu que les mêmes clés apparaissent dans plusieurs DISTINCT préliminaires. Cette optimisation s’applique lorsque l’expression de partitionnement est une fonction déterministe des colonnes du DISTINCT, et elle est soumise à une heuristique de coût portant sur le nombre de partitions et leur déséquilibre (qu’il est possible de forcer à l’aide du paramètre force_distinct_partitions_independently, désactivé par défaut). Cette même disjonction par partition est propagée à travers les étapes Expression, Filter et ARRAY JOIN jusqu’aux consommateurs finaux DISTINCT, LIMIT BY et GROUP BY, afin qu’ils puissent eux aussi faire l’économie de leur fusion. Ce comportement est contrôlé par les nouveaux paramètres allow_distinct_partitions_independently (activé par défaut) et max_number_of_partitions_for_independent_distinct, ce dernier permettant d’ajuster l’heuristique. #108326 (nihalzp).
  • Les filtres d’exécution sont désormais construits sur les clés d’égalité, même lorsque la clause ON contient également des prédicats de non-égalité, ce qui réduit le nombre de lignes entrant dans la jointure par hachage pour les jointures mixtes (égalité et non-égalité). LEFT ANTI JOIN n’est pas concerné. #108579 (29antonioac).
  • Abaissement du seuil de prélecture logicielle pour les tables de hachage d’agrégation et de jointure, de 4 * L2 cache size à L2 cache size, de sorte que la prélecture est activée dès que la table de hachage ne tient plus dans le cache L2. Cela corrige une régression sur les opérations GROUP BY et JOIN portant sur des tables de hachage de taille moyenne (~1-8 MiB) sur les plateformes dont le cache L2 signalé est important (par exemple aarch64 avec un L2 de 2 MiB). #108655 (groeneai).
  • Amélioration des performances des fonctions de fenêtre. Les requêtes de fenêtre sur des tables larges incluant SELECT * consomment désormais nettement moins de mémoire et s’exécutent plus rapidement. Les agrégats de fenêtre, notamment quantile*, uniq* et groupArray, sont plus rapides avec OVER () et OVER (ORDER BY ... RANGE ...). La fonction cume_dist est également plus rapide, et les fenêtres utilisant PARTITION BY ou ORDER BY sont désormais plus performantes sur les colonnes comportant de nombreuses valeurs répétées. #108688 (nihalzp).
  • Les requêtes d’agrégation sans fonctions d’agrégation utilisent désormais des méthodes basées sur HashSet au lieu de HashMap (pour les types de clés pris en charge). Des accélérations allant jusqu’à 1,8x ont été observées. #108862 (nickitat).
  • Réduction de la surcharge liée à la contention de verrous dans l’algorithme de jointure parallel_hash. #108938 (m-selmi).
  • Ajout d’une nouvelle valeur parallel_full_sorting_merge pour join_algorithm : pour les jointures d’égalité compatibles avec le hachage, elle découpe une jointure full sorting merge join, selon le hachage des clés de jointure, en jointures par fusion indépendantes par fragment, exécutées sur tous les threads. Elle conserve la faible consommation mémoire, en flux continu, d’une jointure par fusion tout en la parallélisant (lors d’un benchmark, environ 2,4x plus rapide et environ 3,3x moins gourmande en mémoire que parallel_hash). Les jointures ASOF se rabattent sur un unique full_sorting_merge ; pour les types de clés incompatibles avec le hachage (virgule flottante, JSON, Object, Dynamic), seule la réécriture par dispersion de hachage est omise, et ils peuvent toujours être fragmentés à la source par plages de clé primaire lorsque query_plan_join_shard_by_pk_ranges est activé. Le résultat n’est pas ordonné. #109005 (alexey-milovidov).
  • Les statistiques de colonnes sont désormais matérialisées par défaut lors de l’INSERT lorsque la taille active actuelle de la table, augmentée de la taille du bloc écrit, ne dépasse pas la valeur du nouveau paramètre materialize_statistics_on_insert_max_table_size (25 GiB par défaut) ; la vérification s’effectuant bloc par bloc, un premier chargement massif dans une table vide peut tout de même matérialiser les statistiques pour chaque bloc écrit. L’optimiseur de jointures basé sur les coûts dispose ainsi d’estimations précises pour les tables de dimensions fraîchement chargées, ce qui évite des ordres de jointure pathologiques (par exemple, les requêtes TPC-H Q5 et Q8 n’expirent plus avec un facteur d’échelle de 40), tandis que les grandes tables de faits déjà établies continuent de matérialiser leurs statistiques lors des fusions. #109454 (alexey-milovidov).
  • Optimisation de la fonction d’agrégation uniqCombined dans le cas d’une agrégation sans clé. #109794 (CurtizJ).
  • Optimisation des fonctions d’agrégation uniqHLL12 et uniqCombined. #109831 (CurtizJ).
  • Parallélisation du traitement des lignes non jointes dans l’algorithme de jointure parallel_hash dans deux cas supplémentaires : en présence d’un filtre résiduel et avec de petites clés de jointure (table de hachage à un seul niveau). #110008 (m-selmi).
  • Un filtre est désormais poussé sous une fonction de fenêtre lorsque le prédicat ne référence que les colonnes PARTITION BY de la fenêtre. Un tel prédicat écrit après la fenêtre (un WHERE externe englobant une sous-requête fenêtrée, ou une conjonction dans QUALIFY) atteint désormais le stockage et permet l’élagage par clé primaire / par partition, ainsi que l’utilisation des index de saut et des projections. Par exemple, SELECT ... row_number() OVER (PARTITION BY key ORDER BY ts) AS rn ... QUALIFY rn = 1 AND key = 'x' ne lit plus ni ne fenêtre l’intégralité de la table. #110114 (groeneai).
  • Avec join_use_nulls = 1, un WHERE rejetant les valeurs NULL et référençant des colonnes des deux côtés d’une jointure externe (par ex. WHERE l.k = 42 AND r.k = 42) convertit désormais la jointure en INNER (ou LEFT/RIGHT) et élague la clé primaire, comme avec join_use_nulls = 0. Auparavant, la conversion n’avait pas lieu dans ce cas et les deux tables étaient lues intégralement. #110121 (groeneai).
  • Optimisation des fonctions uniq, uniqExact et uniqHLL12 dans le cas d’une agrégation sans clé. #110150 (CurtizJ).
  • Un JOIN dont la condition ON est constamment fausse (par ex. ON 1 = 2, ON NULL, ou une condition qui se réduit à faux comme a.t = 'A' AND a.t = 'B') ne lit plus le côté qui ne contribue pas au résultat. Une nouvelle optimisation du plan de requête remplace par une source vide les entrées qui ne peuvent produire aucune ligne. Contrôlée par le paramètre query_plan_short_circuit_constant_false_join (activé par défaut). #110234 (groeneai).
  • Parallélisation de la fusion finale des tables de hachage d’agrégation à un seul niveau : l’espace des clés est divisé en partitions de hachage disjointes que les threads fusionnent indépendamment (paramètre enable_parallel_single_level_merge, activé par défaut). Accélère les requêtes GROUP BY dont les tables de hachage par thread restent sous le seuil de passage à deux niveaux, mais dont la fusion représentait l’essentiel du temps d’exécution. #110395 (nihalzp).
  • Les clés d’agrégation composées d’un seul String utilisent désormais une représentation de table de hachage plus compacte, ce qui améliore les charges de travail GROUP BY fortement axées sur les chaînes de caractères. Le nouveau paramètre enable_packed_string_keys_in_aggregation est activé par défaut ; le désactiver, ou définir compatibility sur une valeur inférieure à 26.8, rétablit l’ancien chemin StringHashTable, qui peut être plus rapide pour des clés à très faible cardinalité de plus de 11 octets. #110573 (harikrishnan94). #112089 (harikrishnan94).
  • Amélioration des performances de la full sorting merge join, jusqu’à 1,5 à 3 fois plus rapide dans les scénarios de données clairsemées. #111200 (vdimir).
  • Activation de join_runtime_filter_from_fixed_hash_table lorsque les types de clé de jointure côté sonde et côté construction diffèrent (par exemple, une clé de sonde Nullable avec une clé de construction non Nullable). #111244 (m-selmi).
  • Amélioration des performances des jointures RIGHT et FULL avec l’algorithme parallel_hash lorsque le côté droit contient des clés de jointure NULL ou des lignes filtrées par l’expression ON, grâce à la suppression de la reconstruction des null maps. #111258 (m-selmi).
  • Nouvel algorithme adaptatif pour le GROUP BY parallèle (contrôlé par le paramètre enable_adaptive_aggregator, activé par défaut) : chaque thread agrège dans sa propre table de hachage jusqu’à ce qu’elle contienne adaptive_aggregator_freeze_threshold clés, puis la fige. Ainsi, les clés fréquentes continuent de mettre à jour les petites tables résidant en cache sans aucune coordination, tandis que les clés rares sont acheminées selon leur hachage vers des files d’attente par bucket et agrégées une seule fois, lors de la fusion parallèle par bucket. #111459 (nihalzp).
  • Accélération du hachage ligne par ligne des valeurs de colonnes JSON/Object stockées dans les données partagées (utilisé par exemple par uniq/uniqExact sur une colonne JSON et par les clés hachées de GROUP BY/DISTINCT), grâce à la suppression de la reconstruction d’un ColumnDynamic pour chaque valeur. Les valeurs de hachage restent inchangées. #111501 (valerypetrov).
  • Amélioration des performances des hash JOIN RIGHT et FULL comportant plusieurs disjonctions OR dans la section ON, ainsi que des RIGHT/FULL JOIN avec des conditions résiduelles (d’inégalité). #111590 (m-selmi).
  • La lecture ordonnée à travers une JOIN fonctionne désormais aussi lorsqu’un seuil de déversement automatique sur disque est configuré pour une jointure de hachage (comportement par défaut depuis la version 26.5). Lorsque cette optimisation s’applique, la jointure reste en mémoire afin de préserver l’ordre du côté gauche ; définissez query_plan_read_in_order_through_spilling_join = 0 pour rétablir le comportement précédent, dans lequel ces jointures ne sont pas utilisées pour la lecture ordonnée et peuvent librement déverser sur disque. Par ailleurs, un ORDER BY ... LIMIT sur une LEFT JOIN lue dans l’ordre n’annonce plus la table entière comme nombre de lignes à lire ; il ne déclenche donc plus max_rows_to_read avec read_overflow_mode = 'throw' pour une lecture qui s’arrête prématurément. #111973 (alexey-milovidov).
  • Réactivation de l’optimisation des clés consécutives pour l’agrégation TTL ... GROUP BY lors des fusions. L’agrégateur était construit en passant min_chunk_bytes_for_parallel_parsing (un seuil en octets, 10 MiB par défaut) à la place de min_hit_rate_to_use_consecutive_keys_optimization (un taux de réussite dans [0, 1], 0,5 par défaut), si bien que l’optimisation était systématiquement désactivée sur ce chemin. Les résultats ne sont pas affectés. #112314 (groeneai).
  • La réplication différée des colonnes pour ARRAY JOIN n’est plus perdue lorsqu’un plan de requête est sérialisé. Avec serialize_query_plan = 1, le nœud exécutant un fragment de plan distant ignorait enable_lazy_columns_replication et matérialisait systématiquement chaque copie répliquée, alors même que ce paramètre est activé par défaut. #112330 (groeneai).
  • Une égalité dans la clause WHERE est désormais fusionnée dans la condition de JOIN lorsque ses opérandes ont des types différents mais un supertype commun, comme Int32 et Nullable(Int32). Une jointure CROSS peut ainsi être convertie en jointure INNER. #112630 (m-selmi).
  • Correction d’une réallocation quadratique lors de la réplication d’un Array dont les éléments contiennent des valeurs JSON stockées dans les données partagées, par exemple un Array(JSON) constant passé à une fonction d’agrégation. Ces requêtes copiaient un nombre d’octets qui croissait de manière quadratique avec le nombre de lignes. #112897 (groeneai).
  • Rétablissement de l’élagage des parties de données et des granules lorsque les limites d’un prédicat de plage portant sur une clé primaire ou une clé de partition MergeTree sont fournies via une clause JOIN ON avec un côté constant d’une seule ligne, par exemple SELECT count() FROM t JOIN bounds ON t.d >= bounds.lo AND t.d <= bounds.hi. Depuis la version 25.2, une telle requête lisait toutes les parties de données, et le paramètre query_plan_use_logical_join_step, qui permettait de rétablir l’élagage, est devenu obsolète dans la version 26.5, si bien qu’il n’existait plus aucun moyen de le récupérer. #112957 (groeneai).
  • Correction de la complexité cubique de la planification des requêtes pour une jointure avec une table Merge portant sur de nombreuses tables de structures différentes. Ces requêtes pouvaient auparavant passer plusieurs minutes en planification sans réagir à max_execution_time ni à KILL QUERY. #113140 (alexey-milovidov).
  • Correction d’une régression de performance : une agrégation sans clés GROUP BY sérialisait, à chaque exécution, l’intégralité du sous-arbre de son plan d’entrée, y compris le contenu complet des littéraux issus du pliage de constantes, afin de calculer une clé de cache de taille de table de hachage qu’elle ne peut de toute façon jamais utiliser. Une requête telle que SELECT quantileMerge(arrayJoin(arrayMap(x -> state, range(5000000)))) passait environ 63 ms par exécution dans l’optimisation du plan de requête, contre 0,15 ms désormais. #113333 (groeneai).
  • Les opérateurs d’agrégation PromQL topk, bottomk et limitk sont désormais évalués avec un plan en flux dont l’état de sélection est en O(time_steps * k), au lieu de rassembler toutes les séries dans une seule ligne avec une mémoire intermédiaire en O(time_steps * series^2). Les requêtes portant sur des métriques à forte cardinalité qui échouaient auparavant avec MEMORY_LIMIT_EXCEEDED s’exécutent désormais avec une mémoire bornée, et l’évaluation est parallélisée au lieu d’être monothread. #113656 (nikitamikhaylov).
  • Remplacement de la table de hachage par bucket des fonctions d’agrégation timeSeries*ToGrid par un tableau plat et trié d’échantillons : l’ingestion d’un échantillon devient un simple ajout en O(1) pour les entrées ordonnées (de loin le cas le plus courant), et la copie suivie du tri par bucket lors de la finalisation disparaît. #113681 (nikitamikhaylov).
  • Les sous-expressions PromQL partagées par plusieurs étapes du plan (l’opérande de topk/bottomk/limitk, le côté gauche de or) sont désormais évaluées une seule fois au lieu de deux : le SQL généré à partir de PromQL matérialise maintenant les sous-requêtes partagées. Cela supprime un parcours redondant de la table samples ; topk appliqué à rate devient environ deux fois plus rapide à froid, et les requêtes de règles kubernetes-mixin gagnent de 10 à 29 %. #113772 (nikitamikhaylov).
  • Le cache des conditions de requête est désormais activé par défaut pour les requêtes qui utilisent l’optimisation ORDER BY <column> LIMIT n (TopK). Définissez use_query_condition_cache_for_top_k = 0 pour le désactiver. #111492 #114539 (alexey-milovidov).
  • Parallélisation de la conversion des états d’agrégation par thread du format à un niveau vers le format à deux niveaux avant la fusion. Cela corrige une régression d’un facteur 2 à 3 des requêtes GROUP BY courtes comportant des états d’agrégation volumineux (par exemple COUNT(DISTINCT ...), ClickBench Q10/Q11) sur des machines dotées d’un très grand nombre de cœurs. Cette régression avait été introduite dans les builds de développement de la version 26.7, lorsque la comptabilisation de la mémoire d’agrégation avait été rendue précise. #114691 (alexey-milovidov).
  • Accélération des fonctions de fenêtre sur les tables MergeTree partitionnées : chaque partition est lue via son propre flux et les fenêtres sont évaluées pour chaque partition, ce qui évite la redistribution par hachage qui, d’ordinaire, répartit chaque ligne entre les threads avant le tri de la fenêtre. Cette optimisation s’applique lorsque l’expression de partition de la table est une fonction déterministe des colonnes PARTITION BY de la fenêtre, et elle est soumise à la même heuristique de coût que GROUP BY / DISTINCT pour chaque partition (suffisamment de partitions par rapport à max_threads, aucune partition dominante). Elle est contrôlée par les nouveaux paramètres allow_window_partitions_independently (activé par défaut), force_window_partitions_independently et max_number_of_partitions_for_independent_window. #114783 (nihalzp).
  • Accélération des fonctions d’agrégation timeSeries*ToGrid : le calcul du bucket pour chaque échantillon évite désormais la division Int128, et les échantillons consécutifs appartenant au même bucket ne déclenchent plus de recherche dans la table de hachage. #114889 (nikitamikhaylov).
  • Les échantillons des fonctions d’agrégation timeSeries*ToGrid sont désormais traités par séquences d’échantillons consécutifs tombant dans un même bucket de la grille, ce qui accélère d’un facteur 1,2 à 2 l’étape d’ingestion des échantillons de timeSeriesRateToGrid et des fonctions associées sur des données de séries temporelles triées. #115041 (nikitamikhaylov).
  • Une agrégation sans clés GROUP BY ne répartit plus sa sortie d’une seule ligne sur max_threads flux : les requêtes dont le plan se poursuit après une telle agrégation bénéficient ainsi d’un pipeline nettement plus léger (moins de processeurs à créer, planifier et profiler), sans aucune perte de parallélisme. #115170 (nikitamikhaylov).
  • Évaluation plus rapide des requêtes instantanées PromQL : les fonctions d’agrégation timeSeries*ToGrid utilisent le chemin vectorisé de classification des échantillons pour les grilles à point unique, et timeSeriesIdToGroup remplit directement sa colonne de résultat. #115267 (nikitamikhaylov).
  • Un JOIN dont la section ON contient plusieurs égalités combinées par OR ne matérialise plus les colonnes de clé du côté droit que la requête ne sélectionne pas. #115465 (nickitat).
  • Accélération de la construction de la table de hachage d’une jointure ANY, SEMI ou ANTI lorsque le côté droit répète de nombreuses fois les mêmes clés de jointure : la jointure ne relit plus le paramètre join_any_take_last_row en mémoire pour chaque ligne du côté droit. #115820 (nickitat).
  • Accélération de GROUP BY sans fonctions d’agrégation sur des clés String, FixedString et LowCardinality : la requête ne réécrit plus une valeur de substitution inutilisée dans la table de hachage pour chaque ligne, mais uniquement pour les lignes qui ajoutent une nouvelle clé. #115834 (nickitat).
  • Réduction de l’utilisation de la mémoire et du temps d’optimisation du plan de requête pour les agrégations avec clé portant sur de grands littéraux issus du constant folding. La clé de cache des statistiques de table de hachage d’agrégation est désormais hachée au fil de sa sérialisation, au lieu d’être d’abord accumulée en mémoire ; un plan contenant un grand littéral n’en conserve donc plus de copie pendant l’optimisation. Pour un littéral de 80 Mo, le pic de mémoire diminue d’environ 250 Mo et le temps d’optimisation est divisé par environ 2,5. La clé calculée reste inchangée. #115847 (groeneai).
  • Optimisation des requêtes GROUP BY ... ORDER BY ... LIMIT et GROUP BY ... LIMIT grâce à un tas borné maintenu pendant l’agrégation, qui permet d’élaguer les groupes ne pouvant pas figurer dans le résultat. L’utilisation de la mémoire et le temps d’exécution sont ainsi considérablement réduits pour les regroupements à forte cardinalité. Contrôlé par le paramètre enable_group_by_top_k_optimization. Basé sur l’implémentation initiale de Dmitriy Terenichev #96630 (thevar1able).

Optimisation et exécution des requêtes

  • La lecture ordonnée avec les répliques parallèles utilise désormais la même logique de découpage de la table en max_threads parties que la lecture locale, pour un meilleur parallélisme. #101434 (nickitat).
  • Ajout de l’élagage spatial GeoParquet au niveau des row groups et des pages dans le lecteur Parquet. L’élagage au niveau des row groups ignore les row groups entiers dont la boîte englobante ne chevauche pas la géométrie de la requête. L’élagage au niveau des pages utilise l’index de colonne covering.bbox pour ignorer les pages non pertinentes au sein des row groups. Généralise également la propagation des prédicats spatiaux via IFunctionBase::isSpatialPredicate() et ajoute un GeoFilter qui évalue les prédicats spatiaux lors de la lecture des lignes Parquet. #104435 (bacek).
  • Ajout du paramètre use_constant_folding_in_index_analysis (désactivé par défaut). Lorsqu’il est activé, l’analyse de la clé primaire MergeTree, des index MinMax et des index de saut intègre les constantes de niveau partition dans le prédicat de filtrage, séparément pour chaque partie de données, ce qui améliore l’élagage pour les filtres dont les branches dépendent des valeurs de partition, par exemple (a = 1 AND b >= 1) OR (a = 2 AND b > 10) avec PARTITION BY a. #104582 (Michicosun).
  • Application d’un positionnement exact des lignes pour les requêtes de recherche vectorielle avec vector_search_with_rescoring, avant le calcul exact des distances dans le pipeline de requête. #105591 (skuznetsov).
  • Ajout d’une optimisation du plan de requête qui fait descendre les fonctions réduisant le volume de données (length, lengthUTF8, empty, notEmpty) sous les étapes Sorting et Filter, en remplaçant l’argument volumineux de type String / FixedString par le résultat de taille fixe, qui n’a ainsi plus besoin d’être mis en mémoire tampon lors d’un tri ni copié par un filtre. Contrôlée par le nouveau paramètre query_plan_push_down_volume_reducing_functions, activé par défaut. #106199 (fastio).
  • Accélération du chargement du cache du système de fichiers au démarrage grâce à l’encodage de la taille de chaque fichier de cache dans son nom (<offset>_<size>), ce qui évite un appel stat par fichier. Le chargement des fichiers de cache écrits par des versions antérieures reste pris en charge. #107415 (alexey-milovidov).
  • Amélioration des performances des requêtes de recherche textuelle combinées à un filtre sur la clé primaire. #108114 (CurtizJ).
  • Amélioration des performances de la fonction pointInPolygon avec un polygone constant. Le cache des polygones prétraités utilise désormais comme clé les arguments constants bruts : un polygone constant n’est donc analysé qu’une seule fois (en cas d’échec de cache) au lieu d’être réanalysé pour chaque bloc d’entrée. #108184 (groeneai).
  • Les compteurs de sélectivité de system.predicate_statistics_log ne sont plus maintenus sur le chemin de lecture MergeTree lorsque la fonctionnalité est désactivée (predicate_statistics_sample_rate = 0, valeur par défaut). Cela supprime de chaque lecture une mise à jour atomique par granule ainsi qu’un comptage de bits O(rows) sur le filtre, corrigeant une régression de performances particulièrement visible sur aarch64. #108190 (groeneai).
  • Ajout du paramètre merge_use_batch_sorting_queueMergeTree, qui permet d’utiliser facultativement la file d’attente de tri par batch pour les fusions MergeTree ordinaires, réduisant ainsi le temps CPU et le temps d’exécution réel des charges de fusion pour lesquelles la fusion triée représente un coût important. #108468 (rorylshanks).
  • Accélération de la planification des requêtes avec l’analyseur pour les expressions qui référencent le même alias WITH ou répètent de nombreuses fois la même sous-expression (par exemple des chaînes if/multiIf profondément imbriquées), en évitant de reconstruire les sous-expressions partagées lors de la construction du DAG d’actions. #108523 (novikd).
  • Accélération de l’analyse de requêtes pour les requêtes comportant de nombreuses chaînes AND de comparaisons, ou des chaînes très longues : l’optimisation optimize_and_compare_chain est désormais limitée par un budget de travail (nouveau paramètre optimize_and_compare_chain_max_hash_work) au lieu de calculer le hachage d’une grande partie de l’arbre de requête, et la résolution des lambdas ne recalcule plus les hachages du corps des lambdas pour sa protection contre la récursion. #108757 (alexey-milovidov).
  • Les postprocesseurs servant uniquement au filtrage (if(..., '', token)) construisent désormais le granule à partir des jetons bruts et éliminent les valeurs vides dès la construction, ce qui évite la materialization par occurrence et l’exécution d’ActionsDAG propres au chemin général. #109049 (ahmadov).
  • Suppression d’un goulot d’étranglement monothread dans ShuffleSendStep du plan de requête distribuée : chaque flux amont répartit désormais ses lignes par compartiment de destination de manière indépendante, au lieu de faire transiter l’ensemble du fragment par un unique ScatterByPartitionTransform. #109206 (davenger).
  • Réduction de la surcharge CPU liée à l’évaluation des expressions pour les requêtes comportant un grand nombre de colonnes (par exemple, une recherche vectorielle sur une colonne QBit avec un petit pas, où chaque vecteur se décompose en centaines de sous-colonnes de plans de bits, toutes transmises à un unique appel *DistanceTransposed). #109380 (alexey-milovidov).
  • Descente d’un filtre sous LIMIT BY lorsque le prédicat ne référence que les colonnes clés de LIMIT BY et que la clause conserve au moins une ligne par groupe non vide (LIMIT n BY avec n >= 1 et OFFSET 0). Ces prédicats (écrits sous la forme d’un WHERE externe englobant une sous-requête LIMIT BY) peuvent ainsi atteindre le stockage et bénéficier de l’élagage par clé primaire, des index de saut et des projections, au lieu de lire toute la table et d’appliquer d’abord le top-N par groupe, sans modifier la sémantique des exceptions pour OFFSET, LIMIT 0 BY ou les formes LIMIT BY négatives. #110116 (groeneai).
  • Suppression d’une allocation sur le tas à chaque vérification d’hyperrectangle lors de l’analyse d’index de clé primaire, ce qui accélère le filtrage des marques de 5 à 26 % selon la forme de la requête. #110153 (raimannma).
  • La propagation des filtres Parquet (élagage des groupes de lignes et index de colonnes) prend désormais en charge les conversions entre variantes de DateTime64, entre variantes de Decimal, ainsi qu’entre Float32 et Float64. #110457 (al13n321).
  • Optimisation de la construction des index de saut de type texte et filtre de Bloom avec le tokenizer sparseGrams #110541 (UnamedRus).
  • Prise en compte combinée du coût d’E/S et de la sélectivité dans l’ordonnancement des conditions PREWHERE. Cela corrige une régression de performance de l’exécution de PREWHERE survenant dans certains cas, introduite par https://github.com/ClickHouse/ClickHouse/pull/101275. Fait partie de https://github.com/ClickHouse/ClickHouse/issues/110462 #110695 (Avogar).
  • Réduction du hachage des jetons définis dans le filtre IN/NOT IN du postprocesseur. #111690 (ahmadov).
  • Les fonctions mapContainsKeyLike, mapContainsValueLike, mapExtractKeyLike et mapExtractValueLike utilisées avec un motif non constant consomment moins de mémoire et de temps lorsque enable_lazy_columns_replication est activé. Le motif n’est plus copié de manière anticipée pour chaque entrée de la map au moment de la capture : la copie est différée jusqu’à l’exécution de la lambda interne et libérée plus tôt, si bien que le pic de consommation compte une copie développée du motif en moins. #111749 (diegomestre2).
  • Activation par défaut (et passage en bêta) de deux fonctionnalités sur disque : 1) packed_skip_index_max_bytes = '1M' : les sous-flux d’index de saut d’au plus 1 MiB sont désormais écrits dans une seule archive skp_idx.packed par part, ce qui réduit le nombre d’objets et de requêtes d’écriture sur le stockage d’objet. Compatibilité : seules les parts nouvellement écrites sont concernées, et elles restent entièrement lisibles par les versions plus anciennes ; les versions antérieures à 26.6 n’utilisent tout simplement pas les index packed pour l’élagage (ils sont considérés comme non matérialisés). Définissez packed_skip_index_max_bytes = 0 pour conserver la disposition autonome, avec un fichier par index. 2) compute_exact_num_defaults_for_sparse_columns combiné à optimize_trivial_count_with_sparsity_filter : des compteurs num_defaults exacts par colonne sont persistés et utilisés pour répondre à SELECT count() FROM t WHERE <pred> sans parcourir les données lorsque le prédicat sépare les lignes entre valeurs par défaut et valeurs non par défaut. Compatibilité : l’indicateur exact_num_defaults dans serialization.json est ignoré par les versions plus anciennes, si bien que les parts restent entièrement lisibles après une rétrogradation ; la réécriture du comptage ne s’applique qu’aux parts qui contiennent des compteurs exacts, ce qui garantit des résultats corrects sur les tables comportant des parts hétérogènes. #111816 (Algunenano).
  • Lorsqu’elle ne peut pas appliquer le filtre complet, l’optimisation des conditions transitives n’utilise désormais son filtre supplémentaire que pour l’analyse d’index. Voir le paramètre optimize_and_compare_chain. #112076 (yariks5s).
  • L’API de requête Prometheus (/api/v1/query, /api/v1/query_range) du moteur expérimental TimeSeries exécute désormais les requêtes en parallèle plutôt que sur un seul thread. #112108 (nikitamikhaylov).
  • randomHadamardTransform appliqué à un vecteur constant est désormais évalué une seule fois au lieu d’une fois par ligne. Cela accélère les requêtes de recherche vectorielle qui font pivoter le vecteur de requête avec randomHadamardTransform avant de le comparer à une colonne QBit. #112921 (alexey-milovidov).
  • Rétablit le partage des sets entre les tâches par part d’une même mutation filtrant avec IN (subquery) sur une colonne de clé primaire. enable_sharing_sets_for_mutations est activé par défaut, mais le set construit pour l’analyse de la clé primaire ne participait plus au cache partagé, si bien qu’une table comportant N parts matérialisait le même set N fois. #112941 (groeneai).
  • Dans les plans de requête distribuée (make_distributed_plan), ORDER BY ... LIMIT k utilise désormais la matérialisation paresseuse sur chaque réplique : les colonnes larges ne sont lues que pour les k premières lignes locales de cette réplique, et non pour chaque ligne candidate. #113111 (shankar-iyer).
  • Accélération de la planification des requêtes lorsque les statistiques de colonne uniq_v2 sont utilisées. Le nombre estimé de valeurs distinctes est désormais mis en cache au lieu d’être recalculé à chaque appel, alors que la planification en émet de nombreux par requête. #113357 (groeneai).
  • Le pushdown de filtre fonctionne désormais pour les sous-colonnes Tuple dans les fichiers Parquet et ORC. Un prédicat tel que WHERE tup.1 = 555555 sur file(), s3() ou url() élague désormais les groupes de lignes et les intervalles d’index de lignes à l’aide des statistiques propres à l’élément du tuple, au lieu de lire le fichier entier. #113383 (groeneai).
  • Amélioration des performances des requêtes PromQL sur le moteur TimeSeries : le SQL interne des sélecteurs lit désormais directement les colonnes de clé primaire de la table des échantillons (sans conversions superflues) et vérifie la plage d’horodatage avant le set d’identifiants de séries, ce qui accélère de 30 à 44 % les requêtes à froid faisant un usage intensif des sélecteurs. #113768 (nikitamikhaylov).
  • Les sélecteurs PromQL qui correspondent à toutes les séries d’une métrique filtrent désormais la table des échantillons d’une table TimeSeries à l’aide d’une plage continue de clé primaire sur id lors de l’analyse de l’index, au lieu d’une volumineuse condition id IN <set>, lorsque la structure de l’id est un tuple à deux composantes avec le générateur d’id canonique. Cela élimine le coût prépondérant de l’analyse d’index monothread dans les requêtes PromQL riches en sélecteurs : jusqu’à −45 % de latence à froid sur les formes de requêtes de tableaux de bord et de règles, et −11 % de moyenne géométrique à froid sur l’ensemble de la suite, sur une table de 62 milliards d’échantillons. #114131 (nikitamikhaylov).
  • Les points de terminaison /api/v1/query et /api/v1/query_range de la Prometheus HTTP API évaluent désormais une seule fois une sous-requête PromQL partagée par plusieurs étapes du plan, comme le faisaient déjà les fonctions de table prometheusQuery et prometheusQueryRange. Auparavant, une requête effectuée avec enable_analyzer = 0, ou par un utilisateur dont le profil définit ce paramètre, relisait la sous-requête partagée une fois pour chaque étape qui y faisait référence. #114261 (groeneai).
  • Correction d’une régression introduite dans la version 26.7 : pour une table MergeTree triée par toUnixTimestamp (ou une autre conversion en entier) d’une colonne DateTime64, un simple filtre de plage sur cette colonne n’utilisait plus la clé primaire et lisait tous les granules des parties de données correspondantes. En outre, un filtre tel que toInt64(ts) >= c sur une table triée par la colonne DateTime64 brute utilise désormais la clé primaire. #114413 (alexey-milovidov).
  • Accélération de l’exécution de IN sur des colonnes regroupées (par exemple triées par clé primaire) grâce à la réutilisation du résultat de la ligne précédente pour les lignes consécutives identiques. #114853 (nikitamikhaylov).
  • Correction d’un temps d’exécution quadratique lorsque les fonctions h3kRing, h3ToChildren, h3PolygonToCells et h3PolygonToCellsWithContainment construisent leur résultat sur de nombreuses lignes. Lors d’un test de charge en intégration continue, une requête portant sur 196 000 lignes passait 838 secondes à copier de la mémoire ; elle s’exécute désormais en 5 secondes environ. #115773 (groeneai).
  • Optimisation des recherches inverses dans les dictionnaires. Un prédicat d’égalité constant tel que WHERE dictGet(dict, attr, key_expr) = value est désormais directement réduit par évaluation des constantes en un filtre sur la clé (key_expr = const, key_expr IN [...] ou WHERE 0) au lieu d’être réécrit en une sous-requête IN (SELECT ... FROM dictionary(...)), et le chemin constant de dictGetKeys s’exécute désormais en parallèle. #91164 (nihalzp).
  • Ajout d’un nouveau paramètre merge_tree_generic_exclusion_search_max_steps qui limite le nombre d’étapes que l’algorithme de recherche par exclusion générique consacre à l’analyse de l’index de clé primaire de chaque partie de données. Ce budget est consacré en priorité aux plus grandes plages de clés restantes, de sorte que même un budget réduit suffit à écarter l’essentiel des données ; les plages qui n’ont pas été entièrement analysées sont lues en totalité : les résultats des requêtes restent donc corrects, mais davantage de granules peuvent être lus. La limite est appliquée de manière souple : elle peut être dépassée d’au plus merge_tree_coarse_index_granularity étapes, plus une étape pour chaque plage en laquelle la partie de données est déjà divisée (par exemple, par le cache des conditions de requête). La valeur par défaut 0 signifie un nombre d’étapes illimité. #92779 (EmeraldShift).
  • Les requêtes comportant ORDER BY et LIMIT sur des VIEW reposant sur des tables distribuées bénéficient désormais de l’optimisation par fusion de flux triés. Les clauses ORDER BY/LIMIT externes sont propagées dans la requête interne d’une vue simple, de sorte que chaque shard trie ses données localement et que le coordinateur fusionne des flux déjà triés, au lieu d’effectuer un tri complet sur le coordinateur. #94102 (matanper).

Performances du stockage, des E/S et du lac de données

  • Utilisation des bornes de l’index minmax de partition pour éliminer davantage de granules lors de l’analyse de la clé primaire des tables MergeTree, lorsqu’une colonne de la clé primaire est également une colonne d’entrée de la clé de partition. Par exemple, dans une table MergeTree avec ORDER BY (id, event_time) et PARTITION BY toYYYYMM(event_time), ClickHouse utilisera l’index minmax de partition sur event_time lors de l’analyse d’index de la clé primaire afin de décider plus finement quels granules éliminer. Contrôlé par le nouveau paramètre use_partition_minmax_for_primary_key_pruning (activé par défaut). #103480 (UnamedRus).
  • Accélération des opérateurs qui parcourent un flux trié à la recherche de séquences de valeurs de clé identiques : DISTINCT dans l’ordre, LIMIT BY dans l’ordre, LIMIT BY négatif dans l’ordre, ainsi que les jointures full_sorting_merge et partial_merge. #106502 (nihalzp).
  • Le lecteur Parquet v3 peut désormais ignorer des groupes de lignes entiers à l’aide de la page de dictionnaire (en plus des statistiques min/max et des filtres de Bloom) pour les conditions d’égalité et IN, lorsqu’un bloc de colonne est entièrement encodé par dictionnaire. Contrôlé par le nouveau paramètre input_format_parquet_dictionary_filter_push_down. #106952 (alexey-milovidov).
  • Utilisation de libdeflate pour la compression et la décompression gzip/zlib/deflate, désormais plus rapides (compression ~1,15× avec un meilleur ratio, décompression ~1,4–1,5×) pour les données .gz/HTTP/url/s3 et le codec GZIP de Parquet. #108074 (alexey-milovidov).
  • Accélération des fonctions trimLeft/trimRight/trimBoth avec les espaces par défaut (et de leurs alias ltrim/rtrim/trim) : les lignes consécutives ne nécessitant aucune suppression d’espaces sont copiées en un seul lot, et le parcours des espaces ligne par ligne n’est exécuté que pour les lignes comportant réellement un espace en début ou en fin. #108177 (groeneai).
  • Accélération de la lecture des tables Iceberg grâce au préchargement du fichier manifest suivant pendant l’analyse du manifest courant, ce qui permet de faire se chevaucher les I/O et le travail CPU. #108543 (asya-ch).
  • Les requêtes de recherche vectorielle utilisant le rescoring limitent désormais le calcul exact des distances aux lignes candidates de l’index vectoriel, au lieu d’effectuer un rescoring par force brute des lignes voisines du même granule MergeTree. #108846 (skuznetsov).
  • Ajout du paramètre merge_tree_min_bytes_per_read_stream afin de limiter le nombre excessif de flux de lecture et la surcharge du pipeline lors des parcours MergeTree locaux ordinaires, non ordonnés et portant sur des colonnes étroites, sur les serveurs dotés d’un grand nombre de cœurs. #109035 (jiebinn).
  • Accélération de la sérialisation et des fusions des colonnes JSON dont les données partagées contiennent de nombreux chemins épars. Lors de l’aplatissement des données partagées en colonnes par chemin, flattenAndBucketSharedDataPaths ne parcourt plus chaque colonne de chemin accumulée à chaque ligne et n’insère plus les valeurs par défaut cellule par cellule ; le parcours de tous les chemins ligne par ligne est supprimé et les lacunes sont comblées en masse. #109341 (groeneai).
  • Le protocole natif envoie désormais les colonnes String sous la forme d’un flux distinct de positions d’octets cumulées (la même disposition que celle utilisée par Array pour ses positions), suivi des données concaténées, au lieu d’un préfixe de longueur varint par valeur, dès lors que les deux pairs utilisent la révision de protocole 54489 ou ultérieure. Un client peut alors lire les colonnes String avec une préallocation exacte du tampon et une seule copie en bloc, soit environ 4 fois plus vite qu’avec la disposition par valeur. Les anciens clients et serveurs ne sont pas affectés : la disposition est négociée via la révision du protocole. La même disposition est disponible dans les formats Native et Buffers grâce aux nouveaux paramètres output_format_native_write_string_with_size_stream / input_format_native_read_string_with_size_stream (désactivés par défaut, afin que les formats restent portables). #110320 (vahid-sohrabloo).
  • Les requêtes avec FINAL qui lisent dans l’ordre de la clé primaire (ORDER BY sur un préfixe de la clé de tri) avec un petit LIMIT n’utilisent plus split_intersecting_parts_ranges_into_layers_final. #110431 (KochetovNicolai).
  • Matérialisation différée (matérialisation tardive) pour la lecture de fichiers Parquet depuis le stockage d’objet, y compris les tables Iceberg : pour les requêtes ORDER BY ... LIMIT n, les colonnes qui ne servent ni au tri ni au filtrage ne sont lues que pour les n lignes retenues par le LIMIT. Sur un fichier Parquet de 200 Mo stocké sur S3, SELECT k, s ... ORDER BY k DESC LIMIT 10 lit 3,3 Mo au lieu de 171 Mo (51 fois moins d’IO, 8 fois plus rapide). Contrôlée par le nouveau paramètre query_plan_optimize_lazy_materialization_for_object_storage (activé par défaut ; nécessite également query_plan_optimize_lazy_materialization). #110970 (alexey-milovidov).
  • Les requêtes sur des tables Iceberg comportant de nombreux fichiers de suppression démarrent désormais plus rapidement. ClickHouse lit et décode les fichiers manifest de suppression en parallèle plutôt qu’un par un, ce qui permet à leurs lectures sur le stockage de se chevaucher. Le nouveau paramètre iceberg_delete_manifest_decode_concurrency (valeur par défaut : 4) détermine le nombre de fichiers décodés simultanément. #112679 (asya-ch).
  • Ajout d’une sérialisation Parquet optionnelle des types UInt128/UInt256/Int128/Int256 en DECIMAL afin de permettre l’élagage des groupes de lignes et des pages. #113347 (bobrik).
  • Correction de la multiplication des requêtes de lecture sur les disques distants lorsqu’une étape de la chaîne de lecteurs lit des plages de marques fragmentées, par exemple des plages filtrées par des index primaires ou de saut. #113584 (CurtizJ).
  • Suppression de la vérification redondante du tri lors de l’écriture de parts MergeTree dont les clés de tri sont toutes constantes. #113899 (perfloop-agent).
  • La matérialisation différée pour les requêtes ORDER BY ... LIMIT n s’applique désormais également aux fichiers Parquet locaux lus avec la fonction de table file et le moteur de table File : les colonnes qui ne servent ni au tri ni au filtrage ne sont lues que pour les n lignes retenues par le LIMIT. Si le fichier a été modifié entre les deux passes, la seconde lecture d’un fichier retenu échoue de manière sûre avec la nouvelle erreur FILE_CHANGED_DURING_READ. Contrôlée par le nouveau paramètre query_plan_optimize_lazy_materialization_for_file (activé par défaut). Corrige également l’échec de la matérialisation différée pour le stockage d’objet avec l’erreur Not found column or subcolumn ... in block lorsqu’une sous-colonne demandée (par exemple d’une colonne JSON) est différée. #114262 (alexey-milovidov).
  • Accélère la construction du set pour IN (subquery) sur les tables MergeTree partitionnées en conservant les lignes de chaque partition dans un seul flux et en dédupliquant chaque flux indépendamment : l’unique transformation de remplissage du set — qui hachait auparavant chaque ligne de manière séquentielle — ne traite ainsi plus que des lignes uniques. Cette optimisation s’applique lorsque l’expression de partition est une fonction déterministe des colonnes de sortie de la sous-requête. Elle n’est pas appliquée lorsque la plus grande partition contient plus de deux fois le nombre de lignes de la partition moyenne ; le nouveau paramètre force_creating_set_partitions_independently (désactivé par défaut) permet de contourner cette vérification. Elle est contrôlée par le nouveau paramètre allow_creating_set_partitions_independently (activé par défaut). #114645 (nihalzp).
  • Marque comme table_readonly les tables de journaux système MergeTree non répliquées ayant fait l’objet d’une rotation et dépourvues de TTL, afin d’éviter des opérations d’arrière-plan inutiles. Le paramètre table_readonlyMergeTree interrompt désormais aussi toute tâche d’arrière-plan sur une table MergeTree simple — fusions ordinaires, fusions TTL (DELETE/MOVE/recompression) et fusions de recompression, mutations d’arrière-plan et déplacements de parties de données en arrière-plan — et rejette les commandes de partition qui modifient les données (ATTACH/MOVE/DROP/DROP DETACHED/FETCH/REPLACE PARTITION et MOVE PARTITION ... TO TABLE vers la table), en plus des insertions, des mutations et de OPTIMIZE qu’il rejetait déjà. Par conséquent, une table table_readonly dotée d’un TTL ne libère plus ses données expirées tant que le paramètre est activé. #95079 (matt-metivier).

Efficacité de la mémoire et des ressources

  • Réduction de l’utilisation mémoire de UNLOCK SNAPSHOT : les métadonnées propres à chaque partie de données, inutilisées par le chemin de déverrouillage, ne sont plus chargées, ce qui évite les erreurs de mémoire insuffisante (out-of-memory) pour les snapshots comportant un très grand nombre de parties de données. #111849 (jkartseva).
  • Réduction des allocations de mémoire lors de la lecture des métadonnées de sauvegarde : chaque entrée de fichier est désormais déplacée dans les maps en mémoire au lieu d’être copiée. #111718 (jkartseva).

Performances des fonctions, des types et des formats

  • La mise en forme des valeurs Decimal en texte est beaucoup plus rapide : jusqu’à 190 fois pour un Decimal256 avec une grande échelle, et environ 1,6 fois pour un Decimal64. Sur les processeurs prenant en charge AVX-512 IFMA, la conversion des entiers 64 bits en texte est environ 1,4 fois plus rapide. Correction également de l’arrondi de toDecimalString, qui perdait la retenue provenant de la partie fractionnaire : toDecimalString(toDecimal64('9.995', 3), 2) renvoyait 9.00 et renvoie désormais 10.00. #112457 (thevar1able).
  • Amélioration des performances de l’algorithme de jointure partial_merge sur les clés FixedString, en comparant des séquences de valeurs en une seule fois plutôt qu’en effectuant un appel de comparaison par valeur. #105737 (4ertus2).
  • Les clés LowCardinality de type string non nullables portant sur une seule colonne sont désormais prises en charge nativement dans HashJoin. #107264 (nickitat).
  • Amélioration des performances de replaceAll et replaceRegexpAll lorsque le motif et le remplacement sont chacun constitués d’un seul caractère (par exemple replaceRegexpAll(s, ' ', '_')). Ces remplacements ne modifient plus la disposition des string : la colonne est donc copiée une seule fois et les octets correspondants sont réécrits sur place, au lieu d’exécuter une boucle de recherche pour chaque correspondance. #108178 (groeneai).
  • Accélération de l’analyse de la représentation date-heure canonique YYYY-MM-DD hh:mm:ss en mode best-effort (date_time_input_format = 'best_effort', cast_string_to_date_time_mode = 'best_effort'), qui est le mode par défaut. Cela corrige une régression des performances d’analyse apparue lors du passage de ces valeurs par défaut de basic à best_effort. #108187 (groeneai).
  • Analyse des littéraux de tableaux et de tuples profondément imbriqués en temps linéaire plutôt que quadratique. #108892 (alexey-milovidov).
  • La fonction toStartOfInterval (alias time_bucket, date_bin) est jusqu’à 5 fois plus rapide pour les intervalles SECOND, MINUTE et HOUR. #109729 (raimannma).
  • Les fonctions arraySort et arrayReverseSort sont plusieurs fois plus rapides sur les tableaux numériques (y compris Decimal et DateTime64) lorsqu’elles sont appelées sans lambda. #109832 (raimannma).
  • Accélération de addDays, addWeeks, subtractDays et subtractWeeks sur les valeurs DateTime et DateTime64 dans les time zones à décalage fixe (comme UTC), grâce à un chemin d’exécution rapide purement arithmétique. #109836 (raimannma).
  • Vectorisation de la décompression du codec Delta. Le décodage est désormais de 1,5 à 5 fois plus rapide pour les types de données 8/16/32 bits, ce qui accélère jusqu’à 20 % les parcours de colonnes compressées avec Delta. #110189 (raimannma).
  • Accélération des fonctions de recherche de string (like, position, match, countSubstrings, hasToken, etc.) sur les colonnes Enum avec une aiguille constante : la recherche porte uniquement sur les noms distincts de l’enum, puis les résultats sont reportés sur chaque ligne, au lieu de parcourir toutes les lignes. #110325 (alexey-milovidov).
  • Amélioration des performances des fonctions de recherche de sous-chaînes (position, countSubstrings, multiSearch*, replaceAll/replaceOne, ainsi que leurs variantes insensibles à la casse et UTF-8) avec une aiguille non constante, jusqu’à 1,5x, grâce à l’utilisation, sur le chemin de traitement par ligne, des algorithmes de recherche de chaînes de caractères basés sur SIMD au lieu d’une solution de repli naïve. #110580 (Algunenano).
  • Amélioration des performances de la consolidation et de la fusion des colonnes Dynamic et Object comportant de nombreux variants dynamiques ou chemins JSON. #110583 (cv4g).
  • estimateCompressionRatio plus rapide avec le codec NONE #111107 (rienath).
  • Accélération du tri et des autres opérations reposant sur la comparaison sur les colonnes JSON dont les chemins sont stockés dans les données partagées. Auparavant, chaque comparaison de valeurs matérialisait une colonne Dynamic temporaire et effectuait une désérialisation binaire complète des deux côtés, ce qui rendait ORDER BY sur ces colonnes extrêmement lent. #111894 (groeneai).
  • La division des entiers 256 bits (Int256, UInt256 et Decimal256) calcule désormais le quotient par mots de 64 bits, à l’aide d’une boucle de divisions 128 / 64 lorsque le diviseur tient sur 64 bits et de l’algorithme D de Knuth dans le cas contraire, au lieu de procéder bit par bit avec une division longue par décalage et soustraction. Cela concerne intDiv, modulo, l’arithmétique Decimal256 et la conversion de ces types en texte. #112477 (thevar1able).
  • La lecture d’une colonne Nested à partir d’un type encodé en binaire (Native, RowBinary avec input_format_binary_decode_types_in_binary_format) ne reconvertit plus le type en nom de type pour l’analyser à nouveau. De plus, un nom de type Tuple imbriqué mal formé est désormais signalé comme une erreur de syntaxe, au lieu de prendre un temps exponentiel en fonction de sa profondeur d’imbrication et d’échouer avec TOO_SLOW_PARSING. #112560 (alexey-milovidov).
  • Accélération des requêtes PromQL sur les tables TimeSeries : timeSeriesIdToGroup réutilise désormais le groupe de la ligne précédente lorsque des lignes consécutives ont le même identifiant de série, ce qui évite la sérialisation de l’identifiant et la recherche par hachage pour chaque ligne. #113580 (nikitamikhaylov).

Autres améliorations des performances

  • Implémentation d’un lecteur et d’un rédacteur natifs pour les formats Arrow et ArrowStream qui n’utilisent pas la bibliothèque Apache Arrow, évitant ainsi des copies et des conversions de données supplémentaires. Ils sont désormais utilisés par défaut (paramètres input_format_arrow_use_native_reader et output_format_arrow_use_native_writer) et sont plus rapides en lecture comme en écriture. #106522 (alexey-milovidov).
  • Optimisation des prédicats nullIf(key, sentinel) = const afin de construire des plages de clés et de permettre l’élagage par clé primaire, par partition et par index de saut. #107308 (adityaksolves).
  • Performances de `INTERSECT ALL` et `EXCEPT ALL` (le mode par défaut de `INTERSECT` et `EXCEPT`) multipliées par plusieurs, grâce à l’indexation du multiensemble sur la valeur de la ligne plutôt qu’au calcul du hachage de chaque ligne avec SipHash. #107649 (Algunenano).
  • Accélération des fonctions match, extract, extractAll, replaceRegexpOne et replaceRegexpAll pour les expressions régulières simples, grâce à leur compilation en code natif avec LLVM. Ce comportement est contrôlé par le nouveau paramètre compile_regular_expressions (activé par défaut) ; les motifs ne relevant pas du sous-ensemble pris en charge basculent de manière transparente vers le moteur RE2. #108004 (alexey-milovidov).
  • Accélération de la décompression ZSTD sur AArch64 (ARM) pour les colonnes présentant de petits décalages de correspondance, comme les colonnes d’entiers à largeur fixe, grâce à la vectorisation avec NEON des copies chevauchantes à faible décalage. Par exemple, la décompression d’une colonne UInt64 est jusqu’à environ 2,3 fois plus rapide sur AWS Graviton 4. #108049 (alexey-milovidov).
  • Amélioration de la vitesse de décompression LZ4 pour les colonnes de taille fixe à faible cardinalité. #108175 (nickitat).
  • Amélioration des performances de levenshteinDistance (et editDistance) pour les chaînes de longueur moyenne sur ARM, en évitant les allocations sur le tas pour chaque ligne dans le chemin de code Myers par blocs. #108185 (groeneai).
  • Accélération de l’analyse des nombres à virgule flottante à partir de texte avec precise_float_parsing = 1, désormais aussi rapide, voire plus rapide, que l’analyseur par défaut sur presque toutes les entrées. La bibliothèque intégrée fast_float est également mise à jour vers la v8.2.10. #108205 (Algunenano).
  • Les fonctions d’encodage et de décodage Base64 utilisent désormais simdutf, ce qui améliore leurs performances. #108333 (thevar1able).
  • Optimisation de DISTINCT pour les clés coûteuses à forte cardinalité. #108366 (nihalzp).
  • Amélioration des performances des fonctions mathématiques grâce à l’activation d’optimisations du compilateur qui ne préservent pas errno. #108628 (nickitat).
  • Accélération de estimateCompressionRatio pour les colonnes encodées en T64, en calculant la taille compressée de manière analytique au lieu d’effectuer la compression. #109054 (rienath).
  • Les JOIN peuvent désormais utiliser l’index de clé primaire ou l’index de saut de la table de gauche pour élaguer les granules #109085 (shankar-iyer).
  • Amélioration des performances des fonctions géographiques H3 (h3ToGeoBoundary, h3ToGeo, h3CellAreaM2/h3CellAreaRads2, geoToH3) grâce au calcul conjoint des paires sinus/cosinus et à la suppression des appels trigonométriques redondants dans les transformations de coordonnées. Les résultats restent inchangés (identiques bit à bit). #109399 (alexey-milovidov).
  • Performances jusqu’à 7 fois supérieures pour les comparaisons (<, >, <=, >=) sur les types entiers larges (Int128, UInt128, Int256, UInt256 et les types qui en dérivent, tels que Decimal128), ainsi que pour la conversion des entiers signés en Int256 (jusqu’à 7 fois sur des données de signes mixtes), grâce à un code de comparaison et d’extension de signe sans branchement. #109474 (raimannma).
  • Les fonctions comportant un seul argument Nullable non constant partagent désormais la null map de l’argument avec le résultat, au lieu d’en allouer et d’en fusionner une nouvelle. #110151 (raimannma).
  • Performances des fonctions arrayMin et arrayMax sur les tableaux numériques améliorées d’un facteur d’environ 1,3 à 1,5, grâce à une réduction vectorisée qui remplace la boucle de comparaison élément par élément. #110163 (raimannma).
  • Amélioration des performances de DISTINCT lorsque le flux d’entrée est trié selon un préfixe des colonnes concernées par le DISTINCT, par exemple SELECT DISTINCT a, b FROM table ORDER BY a. Ces requêtes s’exécutent jusqu’à 2,4 fois plus vite. #110170 (nihalzp).
  • Chemins de traitement par lots vectorisés pour groupBitOr/groupBitAnd/groupBitXor et la famille des fonctions de variance (varPop, varSamp, stddevPop, stddevSamp, skewPop, skewSamp, kurtPop, kurtSamp, covarPop, covarSamp, corr) : jusqu’à 4 fois plus rapides avec le combinateur -If sur des conditions imprévisibles, et jusqu’à 3 fois plus rapides sans celui-ci pour la famille des fonctions de variance. #110461 (raimannma).
  • Optimisation de l’analyse de l’index de texte. #110530 (CurtizJ).
  • Accélération de l’analyse des valeurs UUID à partir de texte (par ex. JSONExtract vers LowCardinality(UUID), toUUID, CAST AS UUID) grâce à la validation et à la conversion des chiffres hexadécimaux en une seule passe au lieu de deux. #110625 (groeneai).
  • Accélération de la décompression ZSTD sur x86_64 pour les colonnes présentant de faibles offsets de correspondance, comme les colonnes d’entiers à largeur fixe, grâce à la vectorisation des copies chevauchantes à offset court avec l’instruction SSSE3 pshufb (pendant x86_64 de l’optimisation AArch64 NEON introduite dans #108049). #110909 (thevar1able).
  • groupUniqArray jusqu’à 3 fois plus rapide pour les types numériques grâce au hachage CRC32 (comme dans uniqExact) et à l’inlining de l’insertion ligne par ligne. #110917 (raimannma).
  • Amélioration de l’efficacité des statistiques de colonnes minmax et de base pour les colonnes à virgule flottante. #111221 (cv4g).
  • Les requêtes COUNT() sont désormais renvoyées directement à partir des métadonnées de cardinalité de l’index de texte. #111494 (ahmadov).
  • Accélération de min et max sur les types 128 bits et 256 bits (Int128, UInt128, Int256, UInt256, Decimal128, Decimal256). Jusqu’à 1,8 fois plus rapide pour les entiers larges et jusqu’à 3,4 fois plus rapide pour les décimaux larges. #111965 (raimannma).
  • Correction d’une régression de performance lors de l’ingestion dans system.query_log. La construction des colonnes de type map Settings et asynchronous_read_counters était environ 6 fois plus lente qu’auparavant, ce qui pouvait amener SYSTEM FLUSH LOGS query_log à dépasser son délai d’expiration de 180 secondes sur les serveurs journalisant de nombreuses requêtes avec de nombreux paramètres modifiés. #112210 (groeneai).
  • Ajout d’une mise en cache optionnelle des tokens absents des index de texte. #112742 (rorylshanks).
  • La journalisation asynchrone utilise désormais une file d’attente bornée sans verrou : les threads qui écrivent des messages de journal ne se bloquent jamais sur un mutex et ne réveillent plus les threads de journalisation. #112803 (alexey-milovidov).
  • Utilisation de l’index de clé primaire pour pointInPolygon lorsque l’argument point est une colonne de clé entière de type Point (ou un autre Tuple de deux éléments numériques), par exemple pointInPolygon(coord, [...]) pour une table triée par coord. Auparavant, seule la forme pointInPolygon((x, y), [...]) avec deux colonnes de clé scalaires était analysée. #112956 (alexey-milovidov).
  • text_index_posting_list_apply_mode a désormais pour valeur par défaut lazy : les requêtes sur index de texte décodent ainsi les listes de postings à la demande, à la granularité des blocs compactés et à l’aide d’un curseur, au lieu de les matérialiser immédiatement en bitmaps pour les index de texte dont posting_list_codec est défini. #113521 (CurtizJ).
  • Accélération des transformations vectorisées de tags TimeSeries grâce au remplacement du remappage par hachage par une indexation directe lorsque les ID de groupes de tags occupent une plage compacte. #114244 (fallintoplace).
  • Amélioration des performances des fusions d’index de texte. #114525 (CurtizJ).
  • Utilisation de masques de présence compacts pour les opérateurs d’ensemble PromQL. #115224 (fallintoplace).
  • L’attachement des tables système ne copie plus deux fois les métadonnées de chaque table, y compris la description complète de ses colonnes, dans le seul but de définir le commentaire de la table. #115490 (thevar1able).
  • Accélération des vérifications de séries en double pour les vecteurs de condition PromQL entièrement nuls. #115822 (fallintoplace).
  • Accélération de toUTCTimestamp/to_utc_timestamp et fromUTCTimestamp/from_utc_timestamp sur les arguments DateTime64, qui retrouvent le débit qu’elles avaient après les corrections de dépassement antérieures. Les résultats restent inchangés. #115838 (groeneai).
  • Réduction du coût fixe d’initialisation de la génération de données aléatoires (rand, rand64, randomString, randomFixedString, generateUUIDv4, generateUUIDv7) sur x86-64. Le gain est sensible lorsque les blocs sont petits ; les valeurs générées restent inchangées. #116158 (Algunenano).
  • Il est désormais possible d’activer, lors de l’analyse, le repli anticipé par court-circuit des fonctions intégrées and et or à l’aide du paramètre enable_function_early_short_circuit. Cela permet d’éviter l’exécution de sous-requêtes scalaires count() à une seule ligne devenues inutiles (code mort), tout en préservant l’inférence de type ordinaire et la validation sémantique. #83505 (fhw12345).
  • Utilisation d’un seul appel IO sur le tampon pour les vecteurs trivialement sérialisables. #89842 (Ergus).
  • Activation par défaut de optimize_or_like_chain. #94517 (alexey-milovidov).
  • Amélioration des performances lors de la récupération d’un seul enregistrement utilisateur depuis system.users lorsque le serveur compte de nombreux utilisateurs. #96699 (alistairjevans).

Améliorations

Améliorations des requêtes et du SQL

  • Les CTE matérialisées référencées depuis plusieurs branches d’une requête UNION sont désormais matérialisées une seule fois et partagées entre toutes les branches. Auparavant, chaque branche recevait sa propre copie de la CTE, qui était intégrée (inlined) et évaluée séparément. #102107 (novikd).
  • Le JOIN runtime filter n’est plus appliqué lorsque le côté sonde est estimé de petite taille. Le nouveau paramètre join_runtime_filter_min_probe_rows (par défaut 1000) contrôle ce seuil #104860 (vdimir).
  • EXPLAIN [PLAN] actions=1, compact=1, pretty=1 devient le comportement par défaut. #105036 (Fgrtue).
  • Réduction de la latence d’annulation pour les formes positives de LIMIT BY (LimitByTransform, LimitBySortedStreamTransform) : KILL QUERY et Ctrl+C interrompent désormais le traitement dès la fin du bloc en cours, au lieu d’attendre la fin de l’étape entière. #106070 (rvasin).
  • Les requêtes JOIN sont désormais prises en charge avec les répliques parallèles automatiques. #106073 (nickitat).
  • Ajout d’une nouvelle métrique asynchrone UntrackedMemory (visible dans system.asynchronous_metrics) qui indique la mémoire déjà allouée par les threads mais pas encore comptabilisée dans le compteur global de suivi de la mémoire. Chaque thread accumule localement les petites allocations et les remonte par lots. Cela permet d’expliquer les écarts entre MemoryTracking et la consommation mémoire réelle du processus. Les messages d’erreur MEMORY_LIMIT_EXCEEDED indiquent désormais aussi la quantité de mémoire non suivie, ce qui permet de mieux comprendre pourquoi une requête ou le serveur a atteint sa limite de mémoire. #106386 (mstetsyuk).
  • Lorsqu’un INSERT échoue parce que la table cible TO d’une vue matérialisée rejette l’écriture pendant la construction du pipeline d’insertion (par exemple si la cible est une View ou ne prend pas en charge les transactions), le message d’erreur mentionne désormais la vue matérialisée et sa table cible, au lieu de se limiter à l’erreur de stockage brute. #107234 (groeneai).
  • ALTER TABLE ... MODIFY COLUMN <col> Tuple(...) sur un Tuple nommé ne modifie désormais que les métadonnées lorsqu’il s’agit uniquement d’ajouter des sous-champs, ce qui le rend aussi rapide qu’un ADD COLUMN de premier niveau. Fonctionnalité activable via SET allow_metadata_only_named_tuple_alter = 1. #107305 (amosbird).
  • Ajout du paramètre materialized_postgresql_use_extended_date_and_time_types, défini à la création, pour le moteur de base de données MaterializedPostgreSQL. Par défaut (activé), les colonnes PostgreSQL date/timestamp sont inférées en Date32/DateTime64 ; s’il est défini à 0 lors du CREATE DATABASE, ce sont les types plus restreints Date/DateTime qui sont inférés. Ce paramètre ne s’applique pas au moteur de table MaterializedPostgreSQL. #107428 (alexey-milovidov).
  • EXPLAIN PLAN marque désormais le nombre estimé de lignes produites par une jointure comme approximatif (~~...) lorsque l’estimation provient d’un indice ou d’une valeur de repli aléatoire plutôt que des statistiques de la table, ce qui aide à repérer les plans dépourvus de statistiques fiables pour le réordonnancement des JOIN. #107666 (vdimir).
  • Un INSERT dans une table MergeTree respecte désormais l’annulation de la requête et max_execution_time lors de l’écriture de nombreuses parts, au lieu de risquer de se poursuivre longtemps après avoir été interrompu (kill). #107929 (al13n321).
  • arrayFold respecte désormais l’annulation des requêtes et max_execution_time. Auparavant, un repli (fold) sur un tableau très long (par exemple arrayFold(acc, x -> arrayPushFront(acc, x), range(number), emptyArrayUInt64())) s’exécutait entièrement au sein d’un seul appel de fonction et ne pouvait pas être interrompu : KILL QUERY et les limites de temps étaient donc ignorés jusqu’à la fin du repli. #108192 (groeneai).
  • Ajout de ProfileEvents pour chaque phase de pré-exécution des requêtes : QueryParseMicroseconds, QueryAnalysisMicroseconds, QueryPlanBuildMicroseconds et QueryPipelineBuildMicroseconds. Ils indiquent où le temps est passé avant l’exécution de la requête (analyse syntaxique, analyse, construction du plan de requête, construction du pipeline) et sont disponibles dans system.query_log et system.events. #108282 (jrdi).
  • Les opérations ALTER TABLE qui produiraient des métadonnées de table dépassant max_query_size sont désormais rejetées d’emblée, ce qui évite que des tables ne puissent plus être chargées par des composants tels que la distribution DDL ou la récupération des réplicas. #108283 (lockie).
  • Ajout de ConstantJoin pour les jointures cartésiennes et les jointures à prédicat constant planifiées par l’analyseur : ces requêtes n’échouent plus à cause d’un paramètre join_algorithm incompatible lorsque l’analyseur est utilisé. #108289 (antaljanosbenjamin).
  • Ajout d’une colonne source à system.documentation, contenant le chemin du fichier source où est définie la documentation de chaque entité. La table documente désormais aussi les codecs de compression, les profile events, les métriques courantes, les métriques asynchrones et les tables système elles-mêmes (avec leurs colonnes), et la documentation des paramètres inclut maintenant leur type et leur valeur par défaut. #108346 (alexey-milovidov).
  • Si vous spécifiez une clause WHERE avec des filtres sur table et database, system.iceberg_history ne liste que les bases de données correspondantes. Cela peut faire gagner du temps si le serveur héberge de nombreuses bases de données distantes sans rapport. #108492 (Diskein).
  • Les paramètres de requête (substitutions {name:Type}) peuvent désormais servir de valeurs de paramètres, aussi bien dans la clause SETTINGS d’une requête (comme SELECT ou INSERT) que dans des requêtes SET autonomes, par exemple SELECT ... SETTINGS max_threads = {threads:UInt64} et SET max_threads = {threads:UInt64}. #108760 (alexey-milovidov).
  • Possibilité de calculer le gain combiné des index de saut dans EXPLAIN WHATIF. Le résultat affiche le ratio de données, qui correspond à l’intersection des performances de tous les index hypothétiques adaptés existants. #108934 (yariks5s).
  • Une requête comportant une seule CTE est désormais formatée avec les mêmes sauts de ligne et la même indentation qu’une requête comportant plusieurs CTE. Auparavant, WITH a AS (...) laissait la CTE sur la même ligne que WITH, alors qu’avec deux CTE ou plus, WITH était placé seul sur sa ligne et chaque CTE indentée. #109092 (groeneai).
  • Prise en charge du Query Condition Cache pour les fichiers Parquet locaux lus via le moteur de table File (jusqu’ici réservé au stockage d’objet et aux data lakes). #109247 (alexey-milovidov).
  • Les entrées du Query Condition Cache pour les fichiers Parquet distants (stockage d’objet) sont désormais indexées par ETag en plus du path, afin que l’écrasement d’un objet sur place ne renvoie plus de résultats obsolètes depuis le cache. #109310 (alexey-milovidov).
  • Une référence non qualifiée à une clé d’INNER JOIN mise en égalité dans la condition ON (par ex. SELECT id FROM a INNER JOIN b ON a.id = b.id) n’est plus signalée comme un identifiant ambigu, puisque les deux côtés ont nécessairement la même valeur. #109366 (alexey-milovidov).
  • Les fonctions de distance transposées sur QBit (L2DistanceTransposed, cosineDistanceTransposed, dotProductTransposed, ainsi que leurs variantes quantifiées L2DistanceTransposedQuantized, cosineDistanceTransposedQuantized, dotProductTransposedQuantized) acceptent désormais un vecteur de référence plus long que le nombre de dimensions recherchées, les éléments excédentaires en fin de vecteur étant ignorés. Il est ainsi possible de réutiliser un vecteur de requête complet pour une recherche à dimension réduite (Matryoshka) sans devoir le découper au préalable. #109388 (alexey-milovidov).
  • Ajout d’une vérification garantissant qu’une requête distribuée transmet toujours une version de client connue : une erreur logique est levée au lieu de transmettre silencieusement une version nulle aux shards distants. Les requêtes initiées par le serveur (opérations de flush en arrière-plan, consommateurs de streaming, rechargements de dictionnaires, vidages des insertions asynchrones) indiquent désormais la version du serveur lui-même comme version de l’initiateur. #109408 (alexey-milovidov).
  • Correction de l’utilisation de noms de colonnes qualifiés dans la clause where des mutations sur les moteurs de table de la famille MergeTree. #109491 (Michicosun).
  • Prise en charge des fonctions de fenêtre non partitionnées (OVER (ORDER BY ...)) dans les plans de requêtes distribuées (make_distributed_plan). Auparavant, toute requête comportant une fonction de fenêtre échouait, car WindowStep ne pouvait pas être sérialisé pour une exécution distante ; les fenêtres avec PARTITION BY restent rejetées pour le moment. #109802 (VighneshPath).
  • Il est désormais possible de modifier certains paramètres d’authentification du catalogue one-lake avec ALTER DATABASE ... MODIFY SETTING = 'xxx'. #110019 (alesapin).
  • Une sous-requête placée à droite de IN dont l’unique colonne est un tableau ayant une dimension de plus que l’argument de gauche est désormais interprétée comme l’ensemble des éléments du tableau (comme un littéral de tableau ou une fonction renvoyant un tableau), au lieu d’échouer avec une erreur d’incompatibilité de types peu claire. Par exemple, x IN (SELECT groupArray(x) FROM ...) fonctionne désormais. #110169 (alexey-milovidov).
  • Les requêtes accélérées par le filtrage dynamique TopK (ORDER BY ... LIMIT k) tirent désormais mieux parti du cache des conditions de requête : le cache est alimenté même lorsque la matérialisation différée est appliquée, et ces requêtes peuvent réutiliser des entrées précédemment écrites par une requête ordinaire ayant le même prédicat WHERE. #110507 (shankar-iyer).
  • SHOW CREATE TABLE (ainsi que SHOW CREATE VIEW / SHOW CREATE DICTIONARY) suggère désormais, dans le message d’erreur, une table au nom similaire lorsque la table demandée n’existe pas, comme le font les requêtes SELECT. #110633 (alexey-milovidov).
  • Ajout du modificateur STREAM BOUNDED, qui ne lit que le premier instantané d’une requête de streaming puis se termine, au lieu de s’abonner aux mises à jour. #110653 (SmitaRKulkarni).
  • Correction du cache des résultats de requête pour les requêtes PromQL. Le dialecte promql contournait la vérification des fonctions non déterministes et pouvait renvoyer des résultats obsolètes ancrés sur now(), tandis que l’API HTTP de Prometheus (/api/v1/query, /api/v1/query_range) ne stockait jamais aucune entrée de cache. #110887 (nikitamikhaylov).
  • EXPLAIN ANALYZE indique désormais, pour chaque côté d’une jointure, le nombre de lignes, les taux de correspondance, le facteur d’expansion (fanout) ainsi que l’état propre à l’algorithme, comme la mémoire de la table de hachage et le débordement sur disque. Utilisez matches = 1 pour collecter les métriques de correspondance qui nécessitent un suivi supplémentaire. #110892 (Fgrtue).
  • Correction d’un heap-use-after-free dans l’exécuteur de pipeline, détecté par le fuzzer AST, qui pouvait faire planter le serveur lorsqu’une requête supprime des processeurs à l’exécution (par exemple FINAL avec lectures différées ou tri externe). #111017 (groeneai).
  • Correction d’un use-after-free (erreur de segmentation) lorsqu’une requête utilisant un snapshot de stockage partagé (paramètre enable_shared_storage_snapshot_in_query) retire des parties de données du snapshot — par exemple une sous-requête en streaming lisant la même table MergeTree qu’une autre partie de la requête lit simultanément. #111192 (alexey-milovidov).
  • Prise en charge des plans de requête distribués (make_distributed_plan) pour les requêtes dont on peut prouver, dès la planification, que la source ne produit aucune ligne, comme une lecture depuis une table MergeTree vide. Ces requêtes échouaient auparavant avec SUPPORT_IS_DISABLED. #111941 (groeneai).
  • Correction du query condition cache, qui n’était pas alimenté pour les granules éliminés par un autre conjoint que le dernier d’une clause WHERE comportant plusieurs conditions AND. #112083 (shankar-iyer).
  • Réduction du pic de mémoire lors de l’écriture de colonnes JSON avec une sérialisation des données partagées par buckets (map_with_buckets et advanced), grâce à une répartition des données partagées dans les buckets un par un à l’écriture, au lieu de matérialiser tous les buckets simultanément. Cela profite surtout aux colonnes Array(JSON) : pour le JSON scalaire, les données partagées sont réparties par granule (dans la limite de index_granularity_bytes), si bien que le gain reste faible ; en revanche, un seul granule Array(JSON) peut contenir de nombreuses lignes imbriquées, et donc une grande quantité de données partagées, auquel cas la répartition bucket par bucket réduit sensiblement le pic de mémoire #112172 (Avogar).
  • L’ordonnancement des réservations de mémoire (expérimental) n’évince plus une allocation lorsqu’une libération de mémoire en attente suffirait à couvrir une augmentation de réservation dépassant la limite : la décision d’éviction attend désormais que les diminutions en cours soient appliquées, ce qui évite d’interrompre inutilement des requêtes en situation de pression mémoire. #112299 (serxa).
  • SYSTEM DROP FILESYSTEM CACHE supprime désormais les clés de métadonnées du cache en parallèle, ce qui accélère le vidage des caches du système de fichiers volumineux. Le nouveau paramètre drop_cache_threads du cache de fichiers contrôle le nombre de workers de suppression et vaut 16 par défaut ; avec la valeur 1, la suppression s’effectue dans le thread de la requête. #112532 (kssenii).
  • Les modificateurs d’une déclaration de colonne - COMMENT, CODEC, STATISTICS, TTL, COLLATE, PRIMARY KEY et les SETTINGS par colonne - peuvent désormais être écrits dans n’importe quel ordre dans CREATE TABLE, chacun au plus une fois. Auparavant, un seul ordre fixe était accepté et, par exemple, x UInt64 CODEC(ZSTD) COMMENT 'text' provoquait une erreur de syntaxe. Dans ALTER TABLE ... ADD COLUMN / MODIFY COLUMN, les modificateurs pris en charge - COMMENT, CODEC, STATISTICS, TTL et les SETTINGS par colonne - peuvent également être écrits dans n’importe quel ordre ; les SETTINGS par colonne dans ADD COLUMN et une clause STATISTICS déclarée dans ADD COLUMN / MODIFY COLUMN sont désormais appliqués au lieu d’être supprimés sans avertissement, et COLLATE et PRIMARY KEY dans ces commandes ALTER lèvent désormais une exception au lieu d’être ignorés sans avertissement. #112788 (alexey-milovidov).
  • Une requête sur une table Merge (ou la fonction de table merge) correspondant à de nombreuses tables réagit désormais à KILL QUERY et à max_execution_time pendant la construction des plans de requête de chaque table, et non plus uniquement une fois la planification de toutes les tables enfants terminée. #113415 (alexey-milovidov).
  • Un SELECT sur une table Merge qui dépasse max_execution_time avec timeout_overflow_mode = 'break' cesse désormais de construire les plans de requête pour les tables correspondantes restantes, au lieu d’effectuer l’analyse complète de la requête pour chaque table correspondante avant de s’arrêter. La requête se termine toujours sans erreur, conformément au comportement documenté de ce mode. #113642 (groeneai).
  • Lorsque le type déclaré d’une colonne et ses données divergent lors d’une lecture MergeTree (provenance de types mixte, par exemple après un ALTER TABLE ... MODIFY COLUMN dont la mutation n’est pas terminée), le serveur lève désormais une exception explicite indiquant la colonne et les deux structures, au lieu d’effectuer une conversion non vérifiée : les builds de débogage et avec sanitizers s’interrompaient auparavant avec un simple Bad cast from type A to B sans nommer de colonne, et les builds de production parcouraient silencieusement une mémoire incohérente. #114087 (alexey-milovidov).
  • Activation de ie_join par défaut : la valeur par défaut de join_algorithm est désormais direct,parallel_hash,hash,ie_join. Un JOIN dont la section ON ne contient que des conditions d’inégalité (deux comparaisons <, <=, >, >= entre des expressions des tables jointes) est désormais exécuté avec l’algorithme IEJoin basé sur le tri plutôt qu’avec un CROSS JOIN suivi d’un filtre, et les jointures LEFT/RIGHT/FULL/SEMI/ANTI avec de telles conditions sont prises en charge. Comme ie_join figure en dernière position dans la liste, il n’est utilisé que lorsque les autres algorithmes ne s’appliquent pas. #114327 (vdimir).
  • Propagation du contexte de trace OpenTelemetry dans les fibres utilisées pour l’établissement asynchrone des connexions et l’exécution des requêtes distantes (requêtes couvertes, async_socket_for_remote, async_query_sending_for_remote). Les spans des requêtes distantes d’une requête distribuée sont désormais correctement rattachés au span Connection::sendQuery de l’initiateur, au lieu d’être directement rattachés au contexte de trace fourni par le client, et chaque exécution de tâche asynchrone produit son propre span. #114813 (diegomestre2).
  • DROP DATABASE supprime désormais les tables dans l’ordre topologique inverse des dépendances de chargement et des dépendances référentielles (auparavant, seules les dépendances de chargement étaient prises en compte), de sorte qu’un plantage en cours de DROP DATABASE ne peut plus laisser subsister une table dont les dépendances ont déjà été supprimées. #114952 (alexey-milovidov).
  • Prise en charge des opérateurs de correspondance d’expression régulière de style PostgreSQL : ~ (un alias de la fonction match), ~* (correspondance insensible à la casse), !~ et !~* (négations). Les commandes \d, \dt et \dv de psql fonctionnent désormais lors d’une connexion à ClickHouse via le protocole de compatibilité PostgreSQL, et l’échec d’une requête ne met plus fin à la connexion. #115066 (alexey-milovidov).
  • EXPLAIN WHATIF indique désormais pourquoi l’estimation empirique a été ignorée, dans une nouvelle ligne empirical_reason affichée lorsque empirical_status vaut unsupported. #115140 (yariks5s).
  • Lorsqu’aucun des algorithmes de jointure activés par le paramètre join_algorithm ne peut exécuter un JOIN, le message d’erreur indique désormais les algorithmes essayés ainsi que la rigueur (strictness) et le type du JOIN en échec, au lieu de signaler uniquement qu’aucun d’entre eux n’a fonctionné. #115226 (elkinal).
  • Les requêtes de recherche vectorielle peuvent désormais utiliser l’index de similarité vectorielle lorsque le vecteur de référence est un littéral de tableau d’entiers, par exemple ORDER BY L2Distance(vec, [1, 2]). Auparavant, ces requêtes se rabattaient silencieusement sur un balayage exhaustif. #115255 (rschu1ze).
  • BACKUP ne laisse plus de fichier de verrou dans sa destination lorsque la tentative qui l’a créé échoue avant d’avoir écrit quoi que ce soit, que l’échec survienne lors de la réservation de la destination ou lors de l’ouverture d’une archive. Comme aucune sauvegarde ultérieure ne pouvait correspondre à un tel verrou, toute nouvelle tentative vers la même destination échouait tant que le fichier n’avait pas été supprimé manuellement. Les sauvegardes vers des destinations S3 et Azure créent également le verrou de manière exclusive, et un verrou appartenant à une autre sauvegarde est signalé comme une sauvegarde concurrente plutôt que par l’erreur, quelle qu’elle soit, renvoyée par le stockage. #115387 (jkartseva).
  • Une trace démarrée par échantillonnage (opentelemetry_start_trace_probability) est désormais reportée dans les informations client de la requête, de sorte que les requêtes secondaires distantes et distribuées, ainsi que les entrées DDL, rejoignent la même trace au lieu d’en démarrer de nouvelles, disjointes. #115619 (diegomestre2).
  • Correction de CREATE TABLE pour les tables Iceberg reposant sur Amazon S3 Tables, qui gère lui-même l’emplacement des tables. #115652 (scanhex12).
  • L’agrégation GROUP BY adaptative (enable_adaptive_aggregator) mesure désormais si le report des clés rares est rentable et revient à l’algorithme ordinaire lorsque leurs clés ou leurs arguments d’agrégat sont trop volumineux pour être copiés avec profit, de sorte que les agrégations sur des colonnes de chaînes volumineuses (par exemple min(URL) par utilisateur) s’exécutent avec la vitesse et la consommation mémoire de l’algorithme ordinaire. Ajoute adaptive_aggregator_freeze_threshold_bytes pour limiter en octets la table gelée de chaque thread (la table est gelée dès que l’un des deux seuils, celui-ci ou celui du nombre de clés, est atteint). #115755 (nihalzp).
  • Ajoute le paramètre query_plan_aggregation_bucket_top_k (activé par défaut) pour activer ou désactiver l’optimisation du plan qui ne matérialise que les n meilleurs groupes de chaque bucket à deux niveaux lorsqu’une agrégation finale alimente un ORDER BY sur ses sorties avec LIMIT n. L’optimisation apparaît désormais également dans la sortie de EXPLAIN. #116124 (nihalzp).
  • Amélioration de la prise en charge des requêtes DESCRIBE pour les vues paramétrées. Prise en charge des vues paramétrées dans les expressions scalaires avec le nouvel analyseur. #68978 (novikd).
  • Les requêtes telles que SELECT * FROM t WHERE id utilisent désormais les index de saut de données sur la colonne id. #89603 (adityachopra29).
  • Ajout du paramètre query_plan_merge_expression_into_join, qui permet de fusionner les étapes Expression dans les étapes JOIN lors de l’optimisation par réordonnancement des JOIN. Le réordonnancement des JOIN devient ainsi possible à travers les sous-requêtes qui encapsulent des jointures (par exemple, lorsqu’un JOIN se trouve dans une sous-requête comportant des colonnes calculées), ce qui améliore l’optimisation des arbres de jointures complexes. #98533 (vdimir).
  • Ajout à la table system.grants d’une colonne is_wildcard indiquant si un grant utilise une correspondance de préfixe avec caractère générique (par exemple GRANT SELECT ON db*.*). Auparavant, il était impossible de distinguer dans system.grants les grants avec caractère générique des grants exacts portant sur le même nom. #98577 (il9ue).

Fonctions et types de données

  • Ajout d’un nouveau paramètre allow_lossy_numeric_supertype (désactivé par défaut). Lorsqu’il est activé, if, multiIf, coalesce, ifNull, array et map appliqués à des arguments numériques dépourvus de type commun sans perte (par exemple un Decimal et un Float64, ou un Int64 et un Float64) sont résolus vers le supertype numérique Float64 au lieu d’un Variant, ce qui permet d’utiliser le résultat avec des fonctions d’agrégation comme sum, avg, min et max. Le nom du paramètre est désormais mentionné dans les messages d’erreur des fonctions d’agrégation concernées. #107236 (groeneai).
  • Le quantificateur de tableau SOME / ALL (expr OP SOME(array) / expr OP ALL(array)) prend désormais également en charge les prédicats de comparaison par mots-clés IS DISTINCT FROM et IS NOT DISTINCT FROM, ainsi que les prédicats de recherche dans les chaînes LIKE, ILIKE, NOT LIKE, NOT ILIKE et REGEXP, réécrits en arrayExists / arrayAll. #107454 (alexey-milovidov).
  • Ajout du paramètre input_format_json_max_object_size pour limiter la taille des objets JSON lors de l’analyse. #107669 (Avogar).
  • Correction d’une fuite de mémoire survenant en cas d’échec de l’ouverture d’une base de données SQLite (par exemple, lorsque la fonction de table sqlite ou le moteur de base de données SQLite reçoit un chemin impossible à ouvrir). #107807 (alexey-milovidov).
  • Prise en charge de la fonction length pour le type de données QBit. Elle renvoie la dimension du vecteur sous forme de constante. #108071 (alexey-milovidov).
  • Prise en charge du CAST d’un QBit vers un Array, qui reconstruit le vecteur d’origine. Il s’agit de l’opération inverse de la conversion existante d’Array vers QBit. #108072 (alexey-milovidov).
  • Le moteur de base de données, le table engine et la fonction de table MySQL associent désormais les types de colonnes spatiaux de MySQL (LINESTRING, POLYGON, MULTILINESTRING, MULTIPOLYGON et le type générique GEOMETRY) aux types géométriques ClickHouse correspondants au lieu de String. Ce comportement est contrôlé par le nouvel indicateur geometry du paramètre mysql_datatypes_support_level, activé par défaut. POINT est toujours converti en Point, comme auparavant. La colonne générique GEOMETRY est associée au type englobant Geometry ; la lecture d’une valeur dont le sous-type n’a pas d’équivalent dans ClickHouse (MULTIPOINT, GEOMETRYCOLLECTION) lève une exception au moment de la lecture. #108944 (alexey-milovidov).
  • Utilisation par défaut de l’algorithme d’analyse précis des nombres à virgule flottante (valeur représentable la plus proche) et application du paramètre precise_float_parsing aux formats d’entrée (CSV, TSV, JSON, VALUES, …) et aux littéraux numériques, et non plus uniquement à toFloat*/CAST. Définissez precise_float_parsing = 0 pour retrouver l’ancien comportement, parfois plus rapide mais moins précis. #109086 (Algunenano).
  • Prise en charge de reinterpret d’un Array d’éléments de taille fixe en String, opération inverse du reinterpret existant d’un String/FixedString en Array. #109383 (alexey-milovidov).
  • Autorisation de CAST entre des types QBit dont le type d’élément et/ou le stride diffèrent, tant que la dimension reste identique (par exemple CAST(x AS QBit(Float64, N)) à partir d’un QBit(Float32, N)). Les modifications portant uniquement sur le stride sont sans perte ; les modifications du type d’élément suivent la sémantique de conversion Array correspondante (par exemple, la conversion de Float32 vers BFloat16 peut entraîner une perte de précision, et accurateCast / accurateCastOrNull rejettent les lignes qui ne sont pas exactement représentables). #109387 (alexey-milovidov).
  • Les fonctions quantizeBFloat16ToInt8 et dequantizeInt8ToBFloat16 acceptent désormais aussi des arguments Array et QBit, en plus des surcharges scalaires existantes : elles appliquent alors le codec de Lloyd-Max à l’ensemble du vecteur et renvoient un Array/QBit du type d’élément correspondant. #109398 (alexey-milovidov).
  • Le spécificateur de format de style MySQL %f dans parseDateTime / parseDateTime64 (et leurs variantes OrZero / OrNull) accepte désormais entre 1 et 6 chiffres fractionnaires, interprétés comme des microsecondes alignées à gauche, à l’instar de STR_TO_DATE de MySQL, au lieu d’en exiger exactement 6. Correction également de l’alignement des tableaux PrettyCompact dans les exemples de la documentation des fonctions intégrées. #109421 (alexey-milovidov).
  • La sous-statistique NullCount des statistiques basic est renommée en DefaultCount : au lieu de compter les lignes NULL d’une colonne Nullable, build compte désormais les lignes égales à la valeur par défaut intrinsèque du type de colonne via IColumn::getNumberOfDefaultRows. #109977 (hanfei1991).
  • Réduction de la taille du binaire d’environ 10,5 Mo : les opérations de comparaison et arithmétiques sur des paires de types mixtes rarement utilisées (Decimal face à un entier de largeur différente, et paires impliquant Int128/UInt128/Int256/UInt256) passent désormais par une conversion vers un type commun, au lieu de recourir à un kernel compilé dédié pour chaque combinaison de types. Les paires de même type, les paires couramment utilisées et les opérations plus/minus/multiply, limitées par la mémoire, conservent leurs kernels dédiés ; les résultats, les types de résultat et les cas exceptionnels restent inchangés. #110131 (alexey-milovidov).
  • La lecture depuis un table engine SQLite ou la fonction de table sqlite ne monopolise plus un cœur CPU entier en attente active lorsque la base de données SQLite est verrouillée par une autre connexion. La lecture reste désormais inactive en attendant le verrou, tout en restant annulable. #110248 (groeneai).
  • Arrondi à trois chiffres des valeurs de temps écoulé, de débit et de ratio dans les messages de journal et d’exception, afin que des nombres comme 1.345844286 sec. ne soient plus affichés en pleine précision. #110277 (alexey-milovidov).
  • Correction des lectures MergeTree de colonnes String dotées d’un flux de tailles séparé lorsque les premières lignes sont ignorées, ce qui pouvait renvoyer des données erronées ou entraîner une lecture hors limites dans le cache des sous-flux. Correction également de possibles données erronées lorsque des lectures concurrentes ou répétées accèdent à un sous-objet JSON stocké avec la sérialisation map des données partagées. #110471 (alexey-milovidov).
  • Prise en charge de la clause SETTINGS pour le table engine PostgreSQL et la fonction de table postgresql (par exemple SETTINGS postgresql_connection_pool_size = 50), à parité fonctionnelle avec le moteur MySQL. #110614 (alexey-milovidov).
  • Les fonctions bitmaskToArray et bitmaskToList prennent désormais en charge les arguments (U)Int128 et (U)Int256. Pour les arguments de type grand entier, la fonction bitPositionsToArray s’exécute désormais en un temps proportionnel au nombre de bits à 1, et non plus à la position du bit à 1 le plus élevé. #110743 (raimannma).
  • Correction de la gestion par le codec GCD des valeurs signées à largeur fixe comportant des nombres négatifs. Auparavant, le diviseur était calculé directement à partir des valeurs signées, ce qui pouvait empêcher de trouver le diviseur commun et réduire considérablement l’efficacité de la compression pour les colonnes d’entiers signés et de décimaux. Le diviseur est désormais calculé à partir des valeurs absolues pour les types signés, tandis que le comportement existant est conservé pour les types non signés et pour la décompression. #113798 (kshcherbatov).
  • Les fonctions de fenêtrage avec PARTITION BY fonctionnent désormais avec make_distributed_plan. Lorsque la structure du plan le permet, la fenêtre est exécutée en parallèle sur plusieurs buckets, chacun recevant des partitions complètes. #114111 (VighneshPath).
  • Une fonction de hachage 64 bits est désormais utilisée pour les méthodes d’agrégation externes sur des valeurs nullable à largeur fixe (ce qui évite une explosion des collisions lors d’agrégations à très forte cardinalité). #114210 (nickitat).
  • x IN (subquery) sur une colonne LowCardinality renvoie désormais LowCardinality(UInt8), soit le même type que x IN (literal list). Auparavant, les deux formes renvoyaient des types différents, et les plans distribués comportant un tel IN nécessitaient un traitement particulier lors de la vérification des types du plan. #114229 (davenger).
  • Prise en charge de l’analyse des entiers dépassant la plage 64 bits dans le type de données JSON, JSONExtract et isValidJSON. Ces valeurs sont lues dans des types d’entiers étendus tels que Int128/UInt256, au lieu d’entraîner le rejet du document entier. #114379 (Avogar).
  • Ajout de keyword comme alias du tokenizer array de l’index de texte, pour une meilleure compatibilité avec OpenSearch, Elasticsearch, SolR et Lucene. #115507 (rschu1ze).
  • Les fonctions toDateOrNull, toDateTimeOrNull et toDateTime64OrNull acceptent désormais des arguments entiers de tous les types d’entiers natifs (interprétés de la même manière que par toDate, toDateTime et toDateTime64, avec un argument facultatif de fuseau horaire), et renvoient NULL pour les valeurs hors de la plage du type de résultat. Par exemple, toDateTimeOrNull(1583851242, 'Asia/Shanghai') renvoie 2020-03-10 22:40:42 et toDateTimeOrNull(4294967296) renvoie NULL. #79791 (jitendra1411).

Stockage, lacs de données et stockage d’objet

  • Ajout de métriques Prometheus optionnelles pour l’activité d’éviction du cache du système de fichiers (filesystem_cache_evictions_total, filesystem_cache_evicted_bytes_total, filesystem_cache_evicted_segment_hits, filesystem_cache_evicted_segment_size_bytes, ainsi que leurs variantes par utilisateur étiquetées avec user_id), exposées pour chaque cache via system.dimensional_metrics, system.histogram_metrics et l’endpoint Prometheus. Elles sont contrôlées par les paramètres de configuration du disque de cache expose_prometheus_eviction_metrics et expose_prometheus_eviction_metrics_per_user (tous deux désactivés par défaut), qui peuvent être activés ou désactivés à chaud via SYSTEM RELOAD CONFIG. #105020 (sacheendra).
  • Le moteur Alias prend désormais en charge la lecture avec répliques parallèles lorsque la table cible utilise un moteur de la famille MergeTree. #107830 (nauu).
  • Limitation à max_inflight_parts_for_one_file du nombre de parties préparées simultanément lors de la copie par lecture puis écriture dans Azure Blob Storage (utilisée par les sauvegardes lorsque la copie native n’est pas disponible), afin d’éviter une consommation excessive de mémoire lorsque de nombreux fichiers volumineux sont copiés en parallèle. #108232 (SmitaRKulkarni).
  • Prise en charge de la colonne virtuelle _etag pour le stockage HDFS #108255 (zhangyifan27).
  • Ajout des colonnes settings et engine_settings à system.backups et system.backup_log. settings expose les paramètres propres à la sauvegarde/restauration demandés pour une opération (par exemple allow_s3_native_copy, deduplicate_files, structure_only), et engine_settings expose les paramètres réellement utilisés par le lecteur/l’écrivain du moteur de sauvegarde (par exemple les paramètres de requête S3 tels que allow_native_copy, qui peuvent différer de ceux demandés une fois fusionnés avec la configuration de l’endpoint). Il est ainsi possible de savoir avec quels paramètres une opération BACKUP/RESTORE s’est réellement exécutée. #108334 (jkartseva).
  • Ajout de system.s3_queue_metadata et system.azure_queue_metadata pour inspecter l’état Keeper des tables S3Queue et AzureQueue enregistrées, notamment le nombre de fichiers traités, en cours de traitement et en échec. #108522 (kssenii).
  • Consommation mémoire du cache des en-têtes d’index de texte divisée par 2 à 2,5. Le cache peut ainsi contenir davantage d’en-têtes de parts (paramètre text_index_header_cache_size), ce qui réduit les lectures sur disque pour les requêtes de recherche textuelle. #109332 (CurtizJ).
  • Ajout de colonnes de compatibilité MySQL aux vues INFORMATION_SCHEMA : SCHEMATA.DEFAULT_COLLATION_NAME et DEFAULT_ENCRYPTION ; TABLES.ENGINE et les colonnes MySQL associées ; ainsi que COLUMNS.COLUMN_KEY, PRIVILEGES, GENERATION_EXPRESSION, SRS_ID. Les clients compatibles MySQL peuvent ainsi exécuter leurs requêtes d’introspection du catalogue sans se heurter à l’erreur UNKNOWN_IDENTIFIER. #109351 (alexey-milovidov).
  • Réduction de la consommation mémoire des tables MergeTree comportant de nombreuses parts : les métadonnées par part dérivées du schéma (liste des colonnes, descriptions des colonnes, sérialisations et sous-flux des colonnes) sont désormais partagées entre les parts d’une même table au lieu d’être copiées dans chacune d’elles. Le gain est particulièrement important pour les tables larges comportant de nombreuses petites parts. #109816 (Algunenano).
  • Ajout d’un nouveau paramètre use_projection_index_in_read_pools (désactivé par défaut). Lorsqu’il est activé, les plages de marques entièrement exclues par un index de projection sont supprimées dans les pools de lecture MergeTree avant la création des tâches de lecture correspondantes, au lieu d’être ignorées granule par granule pendant la lecture. Cela évite de créer des tâches de lecture et d’initialiser des lecteurs pour des données qui ne seront pas lues, et permet de mesurer la taille des tâches de lecture en marques restantes. #110291 (KochetovNicolai).
  • Réduction de la verbosité des diagnostics de recherche binaire sur la clé primaire par partie de données dans MergeTree, qui passent de la journalisation de niveau trace au niveau test. #110324 (alexey-milovidov).
  • Correction de reconnexions intempestives des connexions HTTP keep-alive mises en pool via TLS (HTTPS, S3). La vérification des connexions obsolètes utilisait poll sur le socket, qui signale à tort comme fermée une connexion sécurisée active contenant un enregistrement TLS post-handshake non lu (un ticket de session ou KeyUpdate) ; elle utilise désormais un MSG_PEEK non bloquant sur les sockets non chiffrés et SSL_peek/SSL_has_pending sur les sockets TLS, ce qui permet également de détecter correctement une fermeture TLS ordonnée ainsi qu’un enregistrement TLS arrivé seulement en partie. #110402 (alexey-milovidov).
  • Les catalogues OneLake peuvent désormais utiliser des refresh tokens OAuth pour renouveler les access tokens. #110413 (alesapin).
  • Ajout du ProfileEvent DuplicationDataHashComputations, qui compte les calculs de hachage de données par colonne effectués lors de la déduplication des blocs insérés (INSERT) dans les tables *MergeTree. #111173 (valerypetrov).
  • Le SST de l’index UNIQUE KEY est désormais écrit directement dans la partie de données via l’abstraction de stockage, au lieu de transiter par un fichier temporaire local. #111189 (JingYanchao).
  • Prise en charge des écritures concurrentes dans les tables Iceberg sur un disque local : LocalObjectStorage implémente désormais les écritures conditionnelles, de sorte que l’opération compare-and-swap qui publie un nouveau snapshot via version-hint.text y fonctionne et que des écrivains concurrents ne peuvent plus écraser mutuellement leurs mises à jour. #112556 (alexey-milovidov).
  • Le cache de région des buckets du client S3 fonctionne désormais aussi pour les catalogues de data lake ; auparavant, chaque requête vers ces catalogues résolvait de nouveau la région. #113330 (scanhex12).
  • Les défaillances réseau côté client Azure (connexion réinitialisée/refusée, erreurs DNS et TLS) font désormais l’objet de nouvelles tentatives en tant qu’erreurs de transport au lieu de remonter sous la forme d’une erreur 500 synthétique, comme pour le client S3. #115269 (arsenmuk).

Formats et ingestion

  • Prise en charge de la compression snappy dans l’interface HTTP (Accept-Encoding: snappy) et ajout du paramètre snappy_mode, qui permet de choisir entre le format de bloc snappy Hadoop et le format de tramage snappy pour les E/S snappy génériques via file/url. #100752 (alexey-milovidov).
  • Ajout du paramètre input_format_csv_missing_nullable_as_empty_string (désactivé par défaut). Lorsqu’il est activé, une valeur manquante de type Nullable(String) dans une entrée CSV est lue comme une String vide au lieu de NULL, quelle que soit la valeur de input_format_csv_empty_as_default. #107577 (alexey-milovidov).
  • Lors du traitement de messages Protobuf avec input_format_protobuf_oneof_presence, une correspondance exacte des balises n’est plus exigée, afin de permettre les modifications de schéma. #109174 (ilejn).
  • Suppression de l’ancien lecteur ORC (legacy) basé sur Apache Arrow. Le format d’entrée ORC utilise désormais systématiquement le décodeur natif de ClickHouse (qui était déjà utilisé par défaut). Le paramètre input_format_orc_use_fast_decoder est obsolète (il est toujours accepté, mais n’a plus aucun effet). #110074 (alexey-milovidov).
  • Le moteur Hive lit désormais les métadonnées des fichiers ORC (index min/max, nombres de lignes) à l’aide du lecteur ORC natif de ClickHouse, et non plus de l’adaptateur ORC d’Apache Arrow. #110086 (alexey-milovidov).

Sécurité, accès et observability

  • Ajout d’une nouvelle colonne skipping_indices_types à la table system.tables. Elle contient les différents types d’index de saut de données définis pour chaque table. #106388 (CurtizJ).
  • La valeur automatique de max_threads et des paramètres similaires s’affiche désormais dans system.settings sous la forme auto(8) au lieu de 'auto(8)' ; les guillemets simples qui l’entouraient étaient un artefact de longue date intégré à la valeur. La compatibilité entre versions est préservée : l’ancienne forme entre guillemets (legacy) reste acceptée lors de l’analyse des paramètres reçus de serveurs plus anciens. #108657 (alexey-milovidov).
  • Une valeur par défaut de paramètre vide s’affiche désormais sous la forme string vide dans system.documentation au lieu de backticks vides. #108708 (alexey-milovidov).
  • La documentation d’un paramètre dans system.documentation (affichée par la page intégrée /docs et par la commande help) inclut désormais l’historique des modifications de sa valeur par défaut : la version dans laquelle le paramètre a été introduit et chaque modification ultérieure de sa valeur par défaut, avec l’ancienne valeur, la nouvelle valeur et la raison de la modification. #112177 (alexey-milovidov).
  • La documentation de tous les statements SQL (par exemple nom, syntaxe, description, exemples) peut désormais être consultée dans la table système system.statements. #115341 (rschu1ze).

Paramètres et configuration

  • Ajout d’un nouveau paramètre serveur memory_worker_rss_speculative_reserve_ratio (par défaut 1.0, ou 0 dans les constructions avec sanitizer) qui amène le memory tracker global à réserver de manière spéculative ratio * min(resident - previous_resident, resident - tracked) en plus du RSS observé, à chaque cycle de MemoryWorker. Le tracker est ainsi biaisé à la hausse lorsque le RSS croît plus vite que sa comptabilité entre deux échantillons : les allocations reçoivent MEMORY_LIMIT_EXCEEDED plus tôt, et l’OOM-killer du noyau a moins de risques de se déclencher en premier. Définissez le ratio à 0 pour désactiver la spéculation. Les constructions avec sanitizer utilisent 0 par défaut, car l’écart resident - tracked y est principalement dû à la mémoire fantôme (shadow memory) du sanitizer. #104976 (alexey-milovidov).
  • Le mode d’application différée des posting lists de l’index de texte n’est plus expérimental et peut être sélectionné avec text_index_posting_list_apply_mode = 'lazy' sans allow_experimental_text_index_lazy_apply. Le paramètre de seuil de densité text_index_density_threshold a été renommé en text_index_lazy_intersection_density_threshold. #108814 (CurtizJ).
  • Le paramètre merge_selector_enable_heuristic_to_lower_max_parts_to_merge_at_once est désormais activé par défaut : le sélecteur de fusion réduit automatiquement le nombre maximal de parties de données à fusionner simultanément en fonction du taux de remplissage de la partition. #110726 (Michicosun).
  • Les commandes de nettoyage PostgreSQL RESET, UNLISTEN et DISCARD sont désormais acceptées comme des opérations sans effet dans le PostgreSQL wire protocol, au lieu d’échouer avec une erreur de syntaxe. La compatibilité est ainsi améliorée avec des pilotes tels que Skunk, qui envoient RESET ALL et UNLISTEN * lors de l’établissement et du nettoyage de la connexion. #110780 (alexey-milovidov).
  • Les fonctions de texte IA (aiGenerate, aiClassify, aiExtract, aiTranslate) rejettent désormais les réponses du fournisseur tronquées ou incomplètes pour toute autre raison (par exemple lorsque le modèle atteint la limite max_tokens), au lieu de renvoyer silencieusement une sortie partielle. Le comportement est régi par le paramètre ai_function_throw_on_error. #111830 (george-larionov).
  • Amélioration des versions en précision réduite de L2DistanceTransposedQuantized, cosineDistanceTransposedQuantized et dotProductTransposedQuantized, grâce à la reconstruction des codes p < 8QBit(Int8) à l’aide de centroïdes de préfixe fondés sur la moyenne conditionnelle gaussienne. La pleine précision (p = 8) reste exacte au bit près ; les distances approximatives en précision réduite peuvent changer, et les gains en rappel et en latence dépendent de la charge de travail. #111867 (skuznetsov).
  • Le paramètre write_snapshot_version peut désormais être rechargé à chaud. #112150 (alesapin).
  • Ajout de l’observability des piles des fibres, utilisées pour la communication asynchrone avec les répliques distantes : profile events FiberStackAllocs, FiberStackAllocBytes, FiberStackAllocNanoseconds, FiberStackFreeNanoseconds, et métriques FiberStacks, FiberStackBytes. #114541 (alexey-milovidov).
  • Le protocole Prometheus remote-write prend désormais en charge les insertions asynchrones : les données de plusieurs requêtes remote-write simultanées sont regroupées avant la formation des parties de données, selon le paramètre async_insert (activé par défaut ; ajoutez async_insert=0 à l’URL du gestionnaire pour conserver le comportement synchrone). Une requête n’est acquittée qu’une fois les données vidées vers toutes les tables internes de la table TimeSeries cible. #115688 (nikitamikhaylov).

Autres améliorations

  • Ajout de deux paramètres, shrink_over_allocated_columns_min_waste_ratio et shrink_over_allocated_columns_min_waste_bytes, qui permettent aux INSERT de réduire les colonnes surallouées (dont la mémoire réservée dépasse la mémoire utilisée en raison de la croissance par puissances de deux des colonnes de longueur variable) à leur taille réelle avant la materialization et l’écriture des parties de données, ce qui réduit le pic d’utilisation de la mémoire. Désactivés par défaut. #112161 (Avogar).
  • L’étape de lecture des données est désormais entièrement ignorée pour les fusions TTLDrop afin d’éviter une consommation excessive de mémoire, notamment liée aux allocations des tampons de lecture/préchargement. #105859 (Avogar).
  • Ajout de la prise en charge de BFloat16 dans dotProduct et amélioration des performances du produit scalaire grâce au traitement par lots du chemin SIMD. #106569 (nickitat).
  • Les paramètres 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 et max_database_num_to_throw peuvent désormais être modifiés sans redémarrer le serveur. #106821 (UberDever).
  • L’écriture dans les tables DeltaLake, contrôlée par allow_experimental_delta_lake_writes, passe au statut Beta. #107034 (kssenii).
  • Ajout du type de statistiques de colonne uniq_v2, une alternative légère à uniq reposant sur le sketch uniqCombined64. #107863 (hanfei1991).
  • Ajout des ProfileEvents DistributedPlanRemoteTasks, DistributedPlanLocalExecution et DistributedPlanHostsUsed pour suivre l’exécution de make_distributed_plan. #107985 (shankar-iyer).
  • Les requêtes envoyées aux catalogues de data lake REST tels que OneLake incluent désormais un en-tête User-Agent ClickHouse. #108117 (scanhex12).
  • MVTEncodeGeom aligne désormais la géométrie sur la grille de pixels entiers avant le découpage et découpe les polygones avec wagyu : le résultat découpé est ainsi valide (les anneaux auto-intersectants sont réparés) et aligné sur les bords, conformément à ST_AsMVTGeom de PostGIS. #108248 (saarthak2002).
  • Les trois caches d’index de texte sont désormais activés globalement ; auparavant, ils n’étaient activés qu’au niveau des requêtes. Par ailleurs, la taille du cache des listes de postings est désormais nulle (ce qui revient de fait à le désactiver à nouveau), car les listes de postings sont volumineuses et leur mise en cache est trop coûteuse. #108274 (rschu1ze).
  • L’endpoint Prometheus (pour les configurations limitées aux métriques) et la collecte asynchrone des métriques démarrent désormais avant le chargement des tables, afin que les métriques soient observables pendant la phase de chargement des métadonnées, potentiellement longue. #108402 (cwurm).
  • Prise en charge de BFloat16 dans les fonctions mathématiques binaires #108442 (zhangyifan27).
  • Les fonctions de distance vectorielle transposées L2DistanceTransposed, cosineDistanceTransposed et dotProductTransposed appliquent désormais l’optimisation de lecture partielle des plans de bits aux colonnes Nullable(QBit), en ne lisant que les plans de bits demandés au lieu de la colonne entière. #109358 (alexey-milovidov).
  • Prise en charge de arraySum, arrayAvg et arrayProduct pour les tableaux de BFloat16. #109420 (alexey-milovidov).
  • Les fonctions L1Normalize, L2Normalize, LinfNormalize et LpNormalize (ainsi que leurs alias) fonctionnent désormais avec les tableaux, et plus seulement avec les tuples, par cohérence avec L2Norm et les fonctions de distance. #110052 (alexey-milovidov).
  • Les connexions du pool ne sont plus soumises à un ping avant chaque utilisation. Cela supprime l’aller-retour Ping-Pong ajouté à chaque réutilisation d’une connexion, ce qui réduit la latence des requêtes distribuées et de clickhouse-benchmark. Une connexion du pool devenue obsolète est détectée au moyen d’un poll à délai d’expiration nul (une vérification non bloquante qui n’ajoute aucun aller-retour), puis rétablie par reconnexion. #110068 (alexey-milovidov).
  • Envoi des grandes listes de parts pour l’analyse d’index distribuée sous forme de scalaires (évite d’éventuels échecs dus à max_ast_elements/max_query_size et supprime le surcoût d’analyse de l’AST) #110419 (azat).
  • Un clic sur le logo cloud dans le Playground ouvre désormais le site web de Clickhouse dans un nouvel onglet, afin que vous ne perdiez pas votre progression pendant l’exécution des requêtes. #110422 (williamhatcher).
  • La valeur par défaut de auto_statistics_types passe de basic, uniq à basic, uniq_v2 #110878 (hanfei1991).
  • Lorsqu’une assertion de réentrance de RWLockImpl::getLock() se déclenche (RWLock is already locked in exclusive mode / Cannot acquire exclusive lock while RWLock is already locked), le message d’erreur indique désormais les autres query_id qui détiennent actuellement le verrou, ainsi que le nombre de verrous déjà détenus par le query_id demandeur, afin de faciliter le diagnostic de ces erreurs d’ordre d’acquisition des verrous. #110971 (groeneai).
  • randomHadamardTransform calcule désormais une transformation exacte, préservant la longueur, pour toute longueur de vecteur dont le plus grand facteur impair est inférieur ou égal à 64 (par exemple 3584 = 512 * 7, fréquent dans les modèles d’embedding), ce qui étend les familles précédemment prises en charge 2^N, 2^k * {12, 20} et 2^k * 9. Une transformation complète d’une longueur qui ne peut pas être représentée exactement lève désormais une exception, au lieu de compléter silencieusement par des zéros jusqu’à un vecteur plus long ; passez output_dims pour calculer une projection tronquée de longueur arbitraire. #111006 (alexey-milovidov).
  • Renvoi du code HTTP 403 Forbidden (au lieu de 500 Internal Server Error) pour les exceptions ACCESS_DENIED via l’interface HTTP. #111043 (Schum-io).
  • UTMToGeo accepte désormais comme quatrième argument la lettre de bande de latitude MGRS renvoyée par geoToUTM (en plus de l’indicateur entier d’hémisphère), de sorte qu’un résultat de geoToUTM peut être directement reconverti avec UTMToGeo. #111521 (alexey-milovidov).
  • MaterializedPostgreSQL conserve désormais les valeurs PostgreSQL TOAST inchangées lors des mises à jour, au lieu de les remplacer par des valeurs par défaut. #111552 (OrpheusAgent).
  • Ajout des paramètres text_index_max_processed_tokens_before_flush et text_index_max_memory_usage_before_flush pour contrôler le moment où les générateurs d’index de texte effectuent le vidage des segments temporaires. #111573 (rorylshanks).
  • Ajout du modificateur STREAM UNORDERED : ignore le tri par ordre de commit propre à chaque snapshot #111794 (SmitaRKulkarni).
  • Désormais, make_distributed_plan=1 désactive les fonctionnalités qui ne sont pas encore prises en charge.. #112463 (alesapin).
  • Les séquences d’échappement ANSI ne sont plus écrites dans INTO OUTFILE lorsque stdout est un tty #113252 (azat).
  • allow_experimental_delta_kernel_rs a été renommé en allow_delta_kernel_rs. L’ancien nom est conservé comme alias. #113476 (kssenii).
  • Ajout des paramètres enable_alp_codec, enable_sz3_codec, enable_zxc_codec et enable_quantized_codec permettant d’activer individuellement chaque codec de compression expérimental. #113824 (rienath).
  • Signalement d’un débordement de pile au lieu d’un arrêt silencieux : les handlers des signaux fatals s’exécutent désormais sur la pile de signaux alternative, de sorte que SIGSEGV, SIGABRT, SIGILL, SIGBUS, SIGSYS, SIGFPE et SIGTRAP produisent une stack trace même lorsque le thread à l’origine de l’erreur a épuisé sa pile. #113927 (groeneai).
  • Prise en charge du paramètre lookback_delta de l’API HTTP de Prometheus pour les requêtes instantanées et par intervalle. #113971 (fallintoplace).
  • Les tables QueryRunner démarrent désormais leurs worker threads à la demande et les libèrent une fois inactifs, au lieu d’occuper des threads pendant toute la durée de vie de la table. #114522 (mstetsyuk).
  • Avec cette modification, lors de l’utilisation des index WHATIF, la référence fournie par la projection signale not_applicable pour chaque candidat au lieu de faire échouer le statement. #114846 (yariks5s).
  • Optimisation des chaînes AND comportant plusieurs conditions de comparaison sur la même expression : détection des contradictions (par ex. a < 3 AND a > 5 → false) et suppression des conditions redondantes (par ex. a = 3 AND a < 5 → a = 3). #99736 (wudidapaopao).

Corrections de bogues

Correctifs liés à MergeTree, à la réplication et au stockage

  • Correction d’une condition de concurrence entre ALTER TABLE ... RENAME COLUMN et un OPTIMIZE TABLE ... FINAL simultané (ou toute fusion en arrière-plan) dans MergeTree, qui pouvait remplacer silencieusement les données de la colonne renommée par des valeurs par défaut. La fenêtre de concurrence entre la publication des nouvelles métadonnées en mémoire et l’enregistrement de la mutation de renommage dans current_mutations_by_version est désormais éliminée : currently_processing_in_background_mutex est maintenu pendant les deux opérations. #104822 (groeneai).
  • Correction de résultats de requête incorrects lors de l’utilisation de GROUP BY avec ORDER BY et arrayJoin dans la projection. #101775 (Onyx2406).
  • Les paramètres de taille de téléversement Azure invalides (par exemple azure_min_upload_part_size = 0 ou azure_max_blocks_in_multipart_upload = 0) sont désormais rejetés en amont avec une erreur explicite, au lieu de déclencher une exception interne (second_size > 0) au plus profond du chemin d’écriture. Cela s’applique à la fois aux paramètres de requête et à la configuration de disque Azure. #103394 (statxc).
  • Levée de BAD_ARGUMENTS au lieu de LOGICAL_ERROR lorsque ALTER TABLE ... MOVE/REPLACE/ATTACH PARTITION est exécuté entre deux tables MergeTree dont les paramètres de granularité sont incompatibles (l’une adaptative, l’autre non). #103881 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR « Expected one block from input stream » levée par KILL QUERY / KILL MUTATION / KILL PART_MOVE_TO_SHARD / KILL TRANSACTION lorsque leur clause WHERE contient une sous-requête évaluée ligne par ligne, ou lorsque max_block_size est suffisamment petit pour que le SELECT interne sur la table system.* concernée produise plus d’un bloc. #104927 (groeneai).
  • Correction d’une fuite dans les vues matérialisées actualisables (MATERIALIZED VIEW ... REFRESH ...) : la table interne temporaire que la tâche de rafraîchissement écarte via EXCHANGE TABLES ne pouvait pas être supprimée si sa taille dépassait la valeur max_table_size_to_drop du serveur. Chaque rafraîchissement ultérieur créait alors une nouvelle table interne temporaire sans parvenir à nettoyer les précédentes, ce qui faisait croître le répertoire de données de la vue jusqu’à saturation du disque. La suppression interne effectuée par la tâche de rafraîchissement ignore désormais max_table_size_to_drop et max_partition_size_to_drop ; ces garde-fous restent appliqués lorsque l’utilisateur exécute lui-même un DROP TABLE sur la vue. #105106 (groeneai).
  • Correction d’une erreur DATA_TYPE_CANNOT_BE_USED_IN_KEY inattendue sur les requêtes ALTER qui ne peuvent pas modifier la clé de tri (paramètres, commentaires, codecs, ajout d’une colonne hors clé, etc.) pour les tables MergeTree dont la clé de tri contient un SimpleAggregateFunction (ou un autre type autorisé uniquement avec allow_suspicious_primary_key = 1). #105111 (groeneai).
  • Un OPTIMIZE ... DRY RUN interrompu par un dépassement du délai de requête (max_execution_time avec timeout_overflow_mode = 'break') renvoie désormais TIMEOUT_EXCEEDED au lieu d’une erreur logique concernant rows_sources (qui faisait planter le serveur dans les builds debug et sanitizer). #107114 (groeneai).
  • Correction de l’erreur NOT_FOUND_COLUMN_IN_BLOCK lors de la lecture de fichiers partitionnés au format Hive avec use_hive_partitioning=1 et une clause WHERE/PREWHERE filtrant une colonne réelle (non virtuelle) alors qu’une colonne de partition Hive est également sélectionnée. #107505 (groeneai).
  • Correction de résultats de requête erronés dus à un élagage incorrect par clé primaire et par partition lorsque toStartOfDay, toStartOfISOYear, toDaysSinceYearZero, toRelativeSecondNum, toRelativeMinuteNum, toRelativeHourNum, toRelativeWeekNum, toRelativeDayNum, toMonthNumSinceEpoch ou toYearNumSinceEpoch est appliquée à une colonne de clé Date32 contenant des dates hors de la plage que la fonction peut représenter (en particulier des dates antérieures au 1970-01-01). Ces fonctions se déclarent monotones auprès de l’index primaire, mais leur résultat débordait auparavant pour de tels arguments, si bien que l’index pouvait élaguer des granules contenant pourtant des lignes correspondantes. Elles saturent désormais aux bornes de leur type de résultat et restent monotones sur toute la plage Date32, ce qui garantit un élagage correct. #108018 (nihalzp).
  • Correction de CREATE TABLE dst AS src SETTINGS ... (et des variantes similaires avec ORDER BY/PARTITION BY mais sans ENGINE explicite), qui perdait silencieusement le moteur, les clauses de stockage (telles que le TTL de table et les clés) et les paramètres de la table source. Le moteur et les clauses de stockage non redéfinies sont désormais hérités de la table source, et les paramètres spécifiés sont fusionnés par-dessus ceux de la table source. #108085 (alexey-milovidov).
  • Correction de l’échec des INSERT dans les tables MergeTree avec l’erreur filesystem error: in rename: Permission denied sur les systèmes de fichiers reposant sur Windows (WSL, CIFS/SMB, montages bind de Docker Desktop). Ce problème était dû au module d’écriture des parties de données, qui laissait des descripteurs de fichiers ouverts pendant le renommage du répertoire de la partie de données. #108089 (alexey-milovidov).
  • Correction d’un blocage sur une table MergeTree rattachée avec un ancien index de saut hypothesis (un type d’index supprimé, conservé uniquement pour la compatibilité avec ATTACH). Auparavant, un DELETE léger, un OPTIMIZE, un simple INSERT ou un SELECT filtré sur une telle table échouait avec ILLEGAL_INDEX (“Index of type ‘hypothesis’ is no longer supported”), et la mutation DELETE était relancée indéfiniment. L’index obsolète est désormais traité comme inerte : il est conservé tel quel lors des fusions et mutations, ignoré lors de l’insertion et de la planification des requêtes, et peut toujours être supprimé avec ALTER TABLE ... DROP INDEX. #108217 (groeneai).
  • Correction de l’erreur NOT_FOUND_COLUMN_IN_BLOCK (par ex. Column _part not found) lors de la sélection de colonnes virtuelles d’une table MergeTree avec des réplicas parallèles, y compris via SELECT * avec asterisk_include_virtual_columns = 1. #108451 (groeneai).
  • Correction de ATTACH PARTITION ... FROM, qui rejetait des tables dont les clés primaires sont équivalentes mais déclarées différemment (l’une par un PRIMARY KEY explicite, l’autre implicitement via ORDER BY). #108590 (jordiori).
  • join_any_take_last_row est désormais respecté par tous les chemins de jointure par hachage pris en charge, y compris les jointures avec déversement automatique sur disque. #108936 (antaljanosbenjamin).
  • Correction de l’erreur NOT_FOUND_COLUMN_IN_BLOCK lorsque query_plan_optimize_lazy_final est activé en même temps qu’un filtre WHERE partiellement déplacé vers PREWHERE (via optimize_move_to_prewhere_if_final). L’optimisation FINAL différée ne demande plus au stockage les colonnes de prédicat calculées par prewhere. #109073 (groeneai).
  • Correction de ALTER TABLE ... ADD PROJECTION sur une colonne ALIAS définie au niveau de la table, qui rendait la table inutilisable (les INSERT suivants échouaient avec UNKNOWN_IDENTIFIER, “Missing columns”) lorsque la session utilisait optimize_respect_aliases = 0. #109091 (groeneai).
  • Correction d’une assertion de pointeur nul (px != 0 dans TreeRewriter::analyzeSelect, qui interrompait le serveur dans les builds de débogage et avec sanitizers) lors de l’exécution de ORDER BY ALL (ou GROUP BY ALL) sur une table utilisant le moteur Merge jointe à une autre table avec l’ancien analyseur (allow_experimental_analyzer = 0). #109139 (groeneai).
  • Correction d’un résultat erroné des sous-requêtes EXISTS corrélées et des sous-requêtes scalaires lorsque la corrélation n’apparaît que dans la projection de la sous-requête, associée à une clause WHERE non corrélée (la sous-requête pouvait être évaluée à false / NULL pour chaque ligne). Corrige https://github.com/ClickHouse/ClickHouse/issues/105760. #109186 (novikd).
  • Correction d’un bogue où la fusion d’une table MergeTree comportant des projections, lorsque le paramètre enable_block_number_column ou enable_block_offset_column était activé, produisait des parts de projection contenant une colonne _block_number/_block_offset superflue. La part de projection fusionnée ne correspondait alors plus à la définition de la projection ni aux parts de projection produites par insertion, si bien que CHECK TABLE et OPTIMIZE ... DRY RUN la signalaient comme corrompue (CORRUPTED_DATA). #109284 (groeneai).
  • Correction de l’erreur UNKNOWN_IDENTIFIER lors de l’exécution de ALTER TABLE ... RENAME/ADD/MODIFY COLUMN sur une table MergeTree disposant d’un index min-max implicite sur les colonnes virtuelles persistantes _block_number ou _block_offset (activé par add_minmax_index_for_block_number_column / add_minmax_index_for_block_offset_column). #109428 (groeneai).
  • Correction de la suppression des index de saut d’une part de données (ou de l’échec de CHECK TABLE avec UNEXPECTED_FILE_IN_DATA_PART) après une mutation réécrivant entièrement la part, telle que ALTER TABLE ... DROP COLUMN sur une colonne MATERIALIZED, appliquée à une part Wide. #109616 (groeneai).
  • Correction d’index de saut (text, bloom_filter, etc.) et de projections obsolètes après la matérialisation de mises à jour légères avec ALTER TABLE ... APPLY PATCHES. Auparavant, les fichiers d’index et de projection dépendant d’une colonne mise à jour par patch restaient inchangés, de sorte que les requêtes qui les utilisaient pouvaient renvoyer des résultats erronés (par exemple, hasToken omettant une ligne mise à jour, ou une projection renvoyant des agrégats obsolètes une fois la part de patch déjà appliquée supprimée). #109709 (groeneai).
  • Correction d’une mutation faisant référence à la colonne virtuelle _sample_factor, qui échouait en cours d’exécution avec Unexpected const virtual column: _sample_factor et restait bloquée. Une mutation lit désormais cette colonne comme valant 1, soit la valeur renvoyée par une requête sans SAMPLE. #109813 (groeneai).
  • Correction de deux erreurs levées lorsqu’une requête avec GROUP BY GROUPING SETS (ou une sous-requête scalaire ou de mutation en contenant une) s’exécute avec force_aggregation_in_order = 1 : Trying to get name of not a column: ExpressionList avec l’ancien analyseur (enable_analyzer = 0), et Memory bound merging of aggregated results is not supported for grouping sets. pour une requête distribuée avec enable_memory_bound_merging_of_aggregation_results = 1 sous le nouvel analyseur. #109862 (groeneai).
  • Correction de trois problèmes liés à use_constant_folding_in_index_analysis : une erreur logique Invalid partition key size pouvant interrompre un SELECT utilisant une projection ordinaire lorsque la table définit part_minmax_index_columns = 'with_block_number_offset' ; des résultats erronés (lignes supprimées silencieusement) pour un filtre sur une clé de partition modulo telle que PARTITION BY id % 200 ; et un plantage (condition de concurrence sur les données) lorsqu’un index text doté d’un tokenizer sparseGrams est interrogé avec un prédicat LIKE sur de nombreuses partitions avec max_threads > 1. #109896 (groeneai).
  • Correction de résultats erronés lorsqu’un index de saut minmax est construit sur une colonne LowCardinality(Nullable(...)) : les prédicats WHERE/PREWHERE/HAVING ... IS NULL transmis au stockage éliminaient auparavant tous les granules et renvoyaient 0 ligne, alors même que la colonne contenait des valeurs NULL. #110061 (groeneai).
  • Correction de la lenteur des requêtes ORDER BY ... LIMIT sur les tables Distributed lorsque prefer_localhost_replica sélectionne une réplique locale. #110136 (EmeraldShift).
  • Correction d’une exception (UNION mode UNION_DEFAULT must be normalized) lorsqu’une mutation DELETE ou ALTER UPDATE utilisait une opération ensembliste (UNION/UNION ALL/UNION DISTINCT/EXCEPT/INTERSECT) dans une sous-requête de sa condition WHERE ou dans une affectation UPDATE. #110196 (alexey-milovidov).
  • Correction de la lenteur de l’arrêt du serveur et de l’absence de réponse de KILL MUTATION lorsqu’une mutation avec x IN (subquery) construisait le set de la sous-requête pendant l’analyse de la clé primaire ; la construction du set est désormais annulée sans délai. #110198 (alexey-milovidov).
  • Correction d’une condition de concurrence entre KILL TRANSACTION et ALTER TABLE ... UPDATE/DELETE exécuté au sein d’une transaction : un rollback survenant entre l’enregistrement de la mutation dans la transaction et son enregistrement dans la table laissait une entrée de mutation orpheline, ce qui provoquait une erreur logique (Cannot find transaction ... that has started mutation ...) dans les tâches d’arrière-plan et bloquait toutes les mutations ultérieures des parts concernées jusqu’au redémarrage du serveur. #110226 (tiandiwonder).
  • Correction de system.tables pour une base de données DataLakeCatalog (Iceberg/Glue) : la requête était entièrement interrompue, ou des tables disparaissaient silencieusement, dès que les métadonnées d’une seule table ne pouvaient pas être résolues. Une telle table reste désormais listée par son nom, avec des valeurs par défaut/NULL pour les colonnes nécessitant l’objet de stockage ouvert (engine, total_rows, etc.), quelle que soit la valeur de database_datalake_require_metadata_access. L’accès direct à la table défaillante signale toujours l’erreur. #110242 (groeneai).
  • L’estimation de la cardinalité pour l’ordre des jointures agrège désormais les statistiques de colonnes sur les parts restantes après l’élagage par partition/clé primaire, plutôt que sur toutes les parts actives. Auparavant, une requête restreinte à une petite partition était planifiée à partir des statistiques de la table entière (une surestimation d’un facteur 2500 du nombre de lignes a été observée, inversant le côté de construction de la jointure par hachage), et la désactivation des statistiques produisait paradoxalement un meilleur plan. Également corrigé : le FINAL paresseux était silencieusement désactivé pour les relations MergeTree sous une JOIN, car le résultat mémoïsé de l’analyse d’index de l’optimiseur d’ordre de jointure était pris à tort pour une projection appliquée. #110283 (skuznetsov-clickhouse).
  • ALTER TABLE ... MOVE/REPLACE PARTITION entre deux tables MergeTree dont les granularités sont incompatibles (l’une adaptative, l’autre non) lève désormais BAD_ARGUMENTS au lieu de LOGICAL_ERROR. Cette situation pouvant être provoquée par l’utilisateur, elle ne doit pas se manifester comme une erreur interne (qui interrompt le serveur dans les constructions debug/sanitizer). #110535 (groeneai).
  • Correction d’un TTL ... DELETE WHERE <cond> qui ne supprimait jamais les lignes devenues conformes à <cond> à la suite d’un ALTER TABLE ... UPDATE sur une colonne référencée dans la clause WHERE. La mutation recalcule désormais le TTL de ligne, de sorte que les lignes expirées nouvellement conformes sont supprimées au lieu d’être conservées silencieusement. #110648 (groeneai).
  • Correction d’une LOGICAL_ERROR “Invalid partition key size: 0” susceptible d’interrompre un INSERT dans une table MergeTree partitionnée avec non_replicated_deduplication_window activé, lorsqu’un lot d’insertion asynchrone n’était que partiellement dédupliqué (certains insert_deduplication_token étant des doublons et d’autres nouveaux). #110651 (groeneai).
  • Correction du constant folding avec le nouvel analyseur, qui modifiait silencieusement le type d’une valeur Geometry constante. Geometry est un Variant nommé dont les alternatives peuvent partager la même disposition de stockage (LineString/Ring, MultiLineString/Polygon, ainsi que les valeurs vides) ; deux constantes évaluées qui ne différaient que par leur alternative étaient donc fusionnées en une seule : par exemple, une constante MULTIPOLYGON EMPTY devenait LINESTRING(). #110709 (groeneai).
  • Correction d’un résultat erroné (fusion mal ordonnée) pour les requêtes avec lecture ordonnée lorsque distinct-in-order ou aggregation-in-order étend la lecture à un préfixe de clé de tri plus long que celui pour lequel ORDER BY avait configuré la virtual row de lecture ordonnée. Les colonnes de tri supplémentaires de la virtual row étaient remplies avec des valeurs par défaut, ce qui pouvait perturber l’ordre du MergingSortedTransform (et provoquer un arrêt avec une LOGICAL_ERROR dans les builds de débogage/sanitizer). #110725 (groeneai).
  • Propagation des rôles actifs d’une requête (SET ROLE / role=) vers les nœuds distants lors des lectures en répliques parallèles et Distributed sur un cluster utilisant un secret interserveur ; auparavant, les rôles par défaut de l’utilisateur étaient utilisés à la place, si bien que les politiques de ligne étaient évaluées de manière incohérente par rapport à l’initiateur. #110867 (evillique).
  • Correction d’une condition de concurrence rare dans laquelle un ALTER ne portant que sur un commentaire ou des paramètres d’une table ReplicatedMergeTree pouvait supprimer silencieusement une colonne ajoutée par un ALTER ... ADD COLUMN concurrent, laissant une réplique avec une structure de la table obsolète. #111029 (tiandiwonder).
  • Correction de l’erreur NUMBER_OF_COLUMNS_DOESNT_MATCH pour un GROUP BY avec une agrégation sur une table Merge encapsulant une table Distributed, lorsque la clé de regroupement est une fonction d’une colonne et qu’une agrégation lit cette même colonne (par exemple SELECT toString(g), min(g) FROM merge_over_distributed GROUP BY toString(g)). #111334 (groeneai).
  • Correction d’un résultat erroné d’un FULL JOIN sur une table à moteur Join (StorageJoin) avec USING lorsque la clé de gauche est Nullable et que la clé de stockage ne l’est pas : une ligne de gauche sans correspondance avec une clé NULL renvoyait 0 au lieu de NULL. #111371 (groeneai).
  • ALTER TABLE ... DETACH PARTITION/PART effectue désormais un fsync de la copie qu’il crée sous detached/ (la sous-arborescence de la partie de données ainsi que la chaîne de répertoires jusqu’à la racine du disque) lorsque le paramètre fsync_part_directory de la table est activé, aussi bien pour le stockage des parties de données Full (répertoire) que Packed. Auparavant, la copie n’était jamais synchronisée par fsync, contrairement à la partie de données couvrante vide qui retire les lignes de l’ensemble actif ; une coupure de courant survenant juste après l’acquittement pouvait donc détruire les données détachées sur btrfs (les entrées de répertoire non synchronisées de la copie étaient annulées, ce qui laissait detached/ vide et ne laissait rien à ATTACH). Ce comportement dépend du paramètre existant fsync_part_directory ; le comportement par défaut reste inchangé. #111426 (groeneai).
  • Une colonne Nullable(Nothing) (ou de tout autre type non autorisé dans les tables) est désormais rejetée dès le CREATE TABLE lorsque le schéma est déduit du stockage (par exemple, une table remote()/Remote/Distributed sans colonnes reposant sur une vue qui exécute SELECT NULL), au lieu de persister des métadonnées dont le chargement échoue au redémarrage suivant du serveur et bloque le chargement de tables sans rapport. #111487 (groeneai).
  • Correction d’une LOGICAL_ERROR (Got read request from replica N for unknown stream ...) susceptible d’interrompre le serveur lors d’une lecture avec répliques parallèles, lorsque la requête est entièrement résolue sur l’initiateur par une optimisation de projection (exact-count, minmax-count, ou projection d’agrégation ou ordinaire stockée ne sélectionnant aucune plage). #111689 (groeneai).
  • Correction de l’INSERT partitionné DeltaLake (allow_experimental_delta_lake_writes=1) qui corrompait les valeurs de partition contenant des caractères de chemin et rejetait NULL. Une valeur comme a/b était silencieusement validée sous la forme a, % produisait un fichier de données illisible, et une valeur de partition NULL échouait avec NOT_IMPLEMENTED. Les valeurs de partition sont désormais encodées en pourcentage (percent-encoding) dans un seul segment de chemin, la valeur réelle est stockée dans partitionValues au lieu d’être réextraite du chemin, et une valeur de partition NULL (ou une chaîne vide) est écrite sous la forme __HIVE_DEFAULT_PARTITION__ avec une entrée partitionValues nulle. #111793 (groeneai).
  • Correction des erreurs logiques Expected UInt64 column for __grouping_set, got BLOB et ColumnBLOB should be converted to a regular column before usage survenant lors d’une requête distribuée exécutée sur un shard avec enable_parallel_blocks_marshalling (activé par défaut), par exemple avec GROUP BY GROUPING SETS sur un cluster disposant d’une réplique locale, ou lors de la lecture d’une table Merge couvrant une table locale et une table distribuée. BlocksMarshallingStep n’est plus ajouté aux plans dont les blocs sont consommés dans le même processus. #111997 (groeneai).
  • Correction d’une LOGICAL_ERROR (Type mismatch when building statistics for column) lors d’une mutation MergeTree sur une table dont le type d’une colonne dotée de statistiques a été modifié par un ALTER MODIFY COLUMN portant uniquement sur les métadonnées (par exemple, Enum8 vers Int8 ou DateTime vers UInt32). La mutation ne recalcule désormais que les statistiques des colonnes qu’elle réécrit effectivement et conserve les autres telles quelles ; une colonne réécrite par MATERIALIZE COLUMN, par un UPDATE d’une colonne dont dépend une colonne MATERIALIZED, ou par CLEAR COLUMN voit désormais ses statistiques recalculées au lieu de conserver un contenu obsolète. #112009 (groeneai).
  • Corrige la disparition silencieuse de lignes lorsqu’une requête filtre sur intDiv(constant, key) ou divide(constant, key). L’analyse de la clé primaire et des partitions indiquait un sens de monotonie erroné pour une constante divisée par une colonne de clé : des granules correspondants étaient donc élagués et la requête renvoyait moins de lignes sans la moindre erreur. Corrige également intDiv avec un dividende constant non signé dont le bit de poids fort est positionné, cesse de déclarer une monotonie lorsqu’un opérande Decimal fait calculer la division dans la largeur native du décimal, et empêche l’analyse de clé de lever Cannot compare DB::IPv4 with long pour une requête valide qui divise une constante IPv4/IPv6 par une colonne de clé. #112156 (groeneai).
  • Corrige le classement comme vue ordinaire d’une vue paramétrée dont le corps SELECT utilise INTERSECT/EXCEPT, ou une chaîne UNION mêlant UNION DISTINCT et UNION ALL. Une telle vue était créée sans erreur, mais ses métadonnées ne pouvaient pas être relues : son chargement levait Invalid storage definition in metadata file, ce qui rendait la vue définitivement impossible à charger et faisait échouer au démarrage un serveur de débogage ou instrumenté (sanitizer). Par ailleurs, system.tables.parameterized_view_parameters n’indiquait aucun paramètre pour ces vues. #112211 (groeneai).
  • Corrige la non-prise en compte de max_execution_time et de KILL QUERY pendant qu’une requête évalue geohashesInBox. Une telle requête développait chaque ligne d’un bloc avant de pouvoir être interrompue, si bien que l’annulation ne prenait effet que plusieurs minutes plus tard ; la fonction vérifie désormais la limite de temps de la requête au fil du parcours des lignes. Les évaluations au sein d’une expression de table persistée, comme une clé de tri, un index de saut ou un PARTITION BY, ne sont pas couvertes et font l’objet d’un suivi distinct. #112273 (groeneai).
  • Corrige une erreur logique lorsque PREWHERE est utilisé sur une table Merge reposant sur une autre table Merge ou sur une vue matérialisée dont le type de colonne diffère. Corrige également le fait qu’une politique de ligne était ignorée silencieusement sur les tables qui lisent depuis des serveurs distants (une table Distributed, ou une table qui en encapsule une), ce qui pouvait exposer des lignes que la politique devait masquer ; ces requêtes sont désormais rejetées avec une erreur ILLEGAL_PREWHERE. Définissez plutôt ces politiques sur les tables locales sous-jacentes de chaque serveur distant ; notez qu’une telle politique n’est toujours pas appliquée aux lectures transmises avec serialize_query_plan = 1, ce qui constitue un bogue distinct et préexistant. #112326 (PedroTadim).
  • Corrige la lecture d’un index secondaire (de saut) obsolète après un changement de type via ALTER TABLE ... MODIFY COLUMN dont la mutation n’a jamais été exécutée, par exemple parce que KILL MUTATION l’a supprimée : les granules sur disque étaient écrits avec l’ancien type mais décodés avec le nouveau, ce qui levait LOGICAL_ERROR, provoquait des demandes d’allocation de plusieurs exaoctets ou renvoyait silencieusement un résultat erroné. Corrige également un résultat erroné renvoyé par un index portant sur une expression dont la signification change alors que chaque octet stocké reste identique, si bien qu’aucune mutation n’est créée : un MODIFY COLUMN qui ne modifie que le fuseau horaire d’un DateTime, ou uniquement un nom de type personnalisé, comme UInt8 vers Bool. Un tel index est désormais ignoré pour la partie de données concernée. #112484 (groeneai).
  • Corrige une LOGICAL_ERROR (Cannot add transform Resize to Pipes because it has no outputs) survenant lorsqu’une requête d’agrégation sur un cluster s’exécute avec serialize_query_plan = 1 et des répliques parallèles basées sur le plan, l’étape de fusion redimensionnant le pipeline à zéro flux. #112506 (groeneai).
  • Corrige une LOGICAL_ERROR (“Part … contains column … that is absent in table …”) survenant lorsqu’une réplique ReplicatedMergeTree applique une mutation à une partie de données récupérée depuis une autre réplique avant d’appliquer son propre ALTER ... ADD COLUMN en attente. Une telle mutation est désormais reportée jusqu’à l’application de la modification des métadonnées. #112510 (groeneai).
  • Corrige les compteurs de lecture (read_rows, read_bytes, ProfileEvents['SelectedRows'], ProfileEvents['SelectedBytes']) ainsi que les quotas READ_ROWS et READ_BYTES, qui étaient surfacturés pour les requêtes dont l’ORDER BY déverse sur disque. Les lignes relues depuis les fichiers temporaires du tri externe étaient comptabilisées une seconde fois comme des lectures de la source : une requête avec déversement surestimait donc le volume lu et pouvait être rejetée avec QUOTA_EXCEEDED alors même qu’elle respectait son quota. #112645 (groeneai).
  • Corrige la sous-estimation, par ProfileEvents['JoinResultRowCount'], de la taille du résultat d’une JOIN qui déverse sur disque. Les lignes émises depuis les buckets différés n’étaient comptabilisées que dans JoinDelayedJoinedTransformRowCount et manquaient dans le total. Le résultat de la requête a toujours été correct ; seul l’événement de profil était erroné. #112673 (groeneai).
  • Corrige un problème qui rendait max_execution_time et KILL QUERY silencieusement inopérants pour une requête distribuée en cours d’établissement de connexions vers des répliques. Une telle requête s’arrête désormais à la tentative de connexion suivante au lieu de passer par toutes les répliques et tentatives restantes. #112689 (groeneai).
  • Correction d’un problème qui permettait d’appliquer un ALTER sur des tables MergeTree alors qu’une mutation RENAME COLUMN inachevée était encore en cours. Cela pouvait modifier les métadonnées de stockage de manière incompatible et bloquer complètement les mutations ultérieures. #112783 (Michicosun).
  • Correction de résultats erronés d’ORDER BY lors de la lecture d’une table Merge reposant sur des tables Distributed : les lignes pouvaient être renvoyées dans le désordre, et des lignes censées figurer dans le résultat pouvaient manquer lorsque LIMIT était utilisé. #112931 (alexey-milovidov).
  • Correction de résultats erronés lorsque le prefetch de lecture du système de fichiers est activé et qu’une colonne Nested est lue en même temps qu’une sous-colonne supprimée puis rajoutée, sur des tables où share_nested_offsets est activé (comportement par défaut). Une telle requête pouvait renvoyer des valeurs par défaut pour une colonne physiquement présente que les ALTER n’avaient jamais modifiée. Sur le stockage d’objet, où remote_filesystem_read_prefetch vaut 1 par défaut, le problème survient sans modifier aucun paramètre ; sur les disques locaux, il nécessite local_filesystem_read_prefetch = 1. Un autre défaut, indépendant du prefetch, corrompt encore ces lectures lorsqu’une plage s’étend sur plusieurs blocs ; il n’est pas traité ici. #112997 (groeneai).
  • Correction d’une erreur logique Pipeline stuck dans les requêtes qui répartissent les données par partition, comme une window function avec PARTITION BY ou une jointure avec join_algorithm = 'parallel_full_sorting_merge', lorsque la partie aval d’un shard se terminait alors qu’un bloc n’était que partiellement distribué. #113190 (groeneai).
  • Correction d’un blocage des statements ALTER, OPTIMIZE, TRUNCATE et de manipulation de partitions avec alter_sync = 2 sur ReplicatedMergeTree lorsque la table commence à s’arrêter pendant que le statement attend une autre réplique, par exemple en raison d’un DROP DATABASE ... SYNC concurrent. Un tel statement échoue désormais avec UNFINISHED et respecte également max_execution_time et KILL QUERY. #113228 (groeneai).
  • Correction de l’erreur Cannot insert element into Set (NOT_IMPLEMENTED) lorsque read_in_order_use_virtual_row est activé et qu’une requête de lecture ordonnée sur MergeTree combine WITH FILL ... INTERPOLATE avec un filtre IN ou has. #113264 (groeneai).
  • Correction d’une erreur logique (exception) Chunk info was not set for chunk in MergingAggregatedTransform lorsque le paramètre inject_random_order_for_select_without_order_by est activé et qu’une requête d’agrégation lit depuis une table Merge contenant un enfant Distributed : l’enveloppe aléatoire ORDER BY rand() n’est plus injectée dans les requêtes planifiées uniquement jusqu’à une étape intermédiaire. #113266 (alexey-milovidov).
  • Correction d’un problème qui empêchait toute suppression de lignes lorsqu’une table MergeTree possède au moins deux règles TTL ... DELETE WHERE dont l’expression temporelle est identique et qui ne diffèrent que par la condition WHERE. Après une fusion de suppression TTL, la valeur rows_where_ttl_info de la partie de données était réinitialisée à zéro : aucune autre fusion TTL n’était donc plus jamais planifiée pour celle-ci, et les lignes expirées étaient conservées indéfiniment, sans erreur ni message de journal. #113384 (groeneai).
  • Correction d’un problème où max_execution_time et KILL QUERY étaient ignorés pendant qu’une requête évalue geohashesInBox, arrayFold, sleep, base58Decode, h3PolygonToCells ou h3PolygonToCellsWithContainment au sein d’une expression stockée dans les métadonnées de la table, telle qu’une clé de tri, un index de saut ou une clé PARTITION BY. #113456 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (Reading from materialized CTE '...' before its materialization completed - DelayedPortsProcessor gate is missing in the query plan) levée lorsqu’une table Merge comportant plusieurs enfants lit une CTE matérialisée référencée par la requête externe. #113558 (alexey-milovidov).
  • Correction de l’erreur Logical error: 'Required output position N is out of range for pass-through inputs' dans EXPLAIN ANALYZE sur une table Merge comportant plusieurs enfants, avec un PREWHERE portant sur une simple colonne et un WHERE distinct. #113725 (groeneai).
  • Correction d’une erreur logique (Unexpected return type) lorsqu’un filtre sur une vue dont le type de colonne diffère de celui du stockage sous-jacent (par exemple information_schema.tables, où engine est de type Nullable(String), construite sur system.tables, où il est de type String) est poussé vers le stockage. #113984 (alexey-milovidov).
  • Correction de l’erreur Not-ready Set is passed as the second argument for function 'in' lorsqu’une mutation dont le prédicat contient IN (subquery) est annulée, par exemple via KILL MUTATION ou DETACH DATABASE, alors que le set de la sous-requête est encore en cours de construction. #114463 (groeneai).
  • ALTER TABLE ... DROP COLUMN sur une colonne utilisée dans la clé de tri, la clé primaire ou la clé de partition échoue désormais avec ALTER_OF_COLUMN_IS_FORBIDDEN accompagné d’une explication, comme c’est déjà le cas pour ALTER TABLE ... CLEAR COLUMN. Auparavant, l’opération échouait avec une erreur déroutante UNKNOWN_IDENTIFIER: Missing columns issue du recalcul des expressions de clé. Il en va de même pour la suppression ou l’effacement d’un groupe Nested entier via son préfixe commun lorsqu’une colonne du groupe est utilisée dans une clé ; auparavant, cela pouvait détruire silencieusement les données du groupe ou laisser une mutation qui ne se terminait jamais. La suppression d’une colonne utilisée uniquement dans une expression TTL indique désormais également quelle expression TTL elle invalide #114468 (m7kss1).
  • Correction d’un problème où une colonne renommée, supprimée puis ajoutée à nouveau dans un même ALTER renvoyait les valeurs de la colonne supprimée au lieu de sa propre valeur par défaut tant que la mutation était en attente. #114601 (tiandiwonder).
  • Correction de la lecture d’une sous-colonne (.size0, .null, un élément de tuple, String.size) via une table Merge lorsque la table sous-jacente déclare la colonne parente comme ALIAS. Une telle lecture renvoyait la valeur par défaut du type et, lorsque la colonne parente était sélectionnée dans la même requête, elle pouvait renvoyer la valeur d’une colonne sans aucun rapport. #114673 (groeneai).
  • Correction de lignes manquantes dans le résultat d’un SELECT filtré par pointInPolygon sur une clé primaire MergeTree lorsque l’argument polygone est un littéral constant invalide et que validate_polygons = 0. L’analyse de la clé primaire déduisait l’élagage des granules d’un polygone qu’elle n’avait jamais validé, et écartait ainsi des granules contenant des lignes correspondantes. #114710 (groeneai).
  • Correction du formatage de ALTER TABLE ... MATERIALIZE INDEX, MATERIALIZE STATISTICS et MATERIALIZE PROJECTION, qui omettait la clause IF EXISTS : la requête formatée ne correspondait donc pas à la requête analysée. Sont également rejetées quatre charges utiles JSON d’AST ALTER ... STATISTICS que l’analyseur SQL ne peut pas produire et dont le formatage donnait une commande différente. #115099 (groeneai).
  • Correction d’une lecture hors limites des hachages de dictionnaire mis en cache lors d’une agrégation ou d’une jointure sur une clé LowCardinality(String) dont le dictionnaire grossit en cours d’agrégation, situation pouvant se produire via TTL ... GROUP BY. Cela pouvait produire un hachage erroné, ainsi qu’un heap-buffer-overflow signalé dans les constructions avec sanitizer. #115604 (groeneai).
  • CREATE TABLE et ALTER TABLE rejettent désormais les codecs avec perte, tels que SZ3, sur une colonne utilisée dans la clé de partitionnement. #115626 (groeneai).
  • La lecture via une table Merge correspondant à une table Alias dont la cible est une vue paramétrée ne déclenche plus LOGICAL_ERROR: Table has no columns. (qui provoque l’arrêt du serveur dans les constructions de débogage et avec sanitizer). Une telle table signale désormais STORAGE_REQUIRES_PARAMETER, soit la même erreur que la vue paramétrée elle-même. Toute autre table correspondante incapable de fournir des colonnes signale désormais UNSUPPORTED_METHOD au lieu d’une erreur interne. #115896 (groeneai).
  • Correction de l’aperçu à la volée d’une mutation en attente dont l’expression fait référence à _sample_factor. Avec apply_mutations_on_fly = 1 et une clause SAMPLE, l’expression était évaluée avec le facteur d’échantillonnage de la requête de lecture au lieu de la valeur persistée par la matérialisation en arrière-plan. Les lignes prévisualisées différaient donc du résultat matérialisé final, et un ALTER TABLE t DELETE WHERE _sample_factor > 1 en attente pouvait amener une lecture SAMPLE à ne renvoyer aucune ligne. La colonne _sample_factor propre à la requête reste inchangée. #115906 (groeneai).
  • Les fichiers de statistiques dont la version de format est V3 sont désormais lus au lieu d’être rejetés avec ILLEGAL_STATISTICS. Ces fichiers n’ont été produits que par des constructions de master sur une courte période, mais les parties de données qui les contenaient étaient totalement illisibles et, sur un disque en lecture seule, ALTER TABLE ... MATERIALIZE STATISTICS ne peut pas les régénérer. #116030 (alexey-milovidov).
  • Correction de l’erreur Invalid number of columns in chunk pushed to OutputPort (LOGICAL_ERROR) lorsqu’une requête SELECT ... FROM t STREAM ... ORDER BY ... LIMIT n lisait une table comportant une PROJECTION. La matérialisation différée n’est plus appliquée aux lectures STREAM. #116187 (groeneai).
  • ALTER TABLE ATTACH PARTITION FROM n’est plus répliqué pour les bases de données répliquées. #94835 (MikhailBurdukov).
  • Correction d’un arrêt brutal du serveur (dans les builds avec sanitizer) et d’une perte silencieuse de données au démarrage d’une table lorsqu’une part MergeTree transactionnelle annulée (rolled back) chevauche une part validée (committed). PartLoadingTree::add identifie désormais correctement laquelle des deux parts qui se chevauchent a été annulée, l’évince et rattache ses enfants orphelins à la part validée, dans le bon ordre. #100992 (tuanpach).
  • Correction de indexOfAssumeSorted, qui renvoyait des résultats incorrects pour les colonnes Array(LowCardinality(String)) dans les tables MergeTree. #101771 (Onyx2406).
  • Correction d’exceptions LOGICAL_ERROR lors de la lecture de tables de data lake Iceberg ou DeltaLake via des chemins susceptibles d’atteindre le pipeline de lecture sans datalake_table_state épinglé, par exemple lors de mises à jour concurrentes des métadonnées Iceberg ou de lectures merge sur des tables DeltaLake. #102033 (groeneai).
  • Correction de l’erreur Logical error: Parsed partition value: ... doesn't match partition value for an existing part with the same partition ID: ... levée par OPTIMIZE TABLE ... PARTITION ... (et par d’autres requêtes qui résolvent une valeur de partition via convertFieldToTypeImpl) sur les tables dont la clé de partition est de type Time. #106202 (groeneai).
  • Correction d’un problème qui faisait marquer à tort une part de données comme ayant une projection corrompue lorsqu’un lightweight delete ne laissait aucune part de projection — soit parce que la projection était reconstruite sans aucune ligne en sortie, soit parce qu’elle était supprimée (lightweight_mutation_projection_mode = 'rebuild'/'drop'). Cet état corrompu désactivait jusqu’ici l’optimisation par projection pour les requêtes portant sur cette part. #106273 (tiandiwonder).
  • Correction des avertissements de surcharge mémoire/CPU/mutations dans system.warnings, qui accusaient un cycle de retard sur les métriques asynchrones (et étaient absents lors du premier cycle). #108035 (alexey-milovidov).
  • Correction d’une exception LOGICAL_ERROR (ScatterExchangeStep should have one source shard, got 8) et de lignes dupliquées lors de l’interrogation d’une table Merge reposant sur des tables Distributed ou des vues avec le paramètre expérimental make_distributed_plan = 1. #108401 (groeneai).
  • Correction d’un arrêt brutal du serveur (Sort order of blocks violated) pendant une fusion, ainsi que de lectures incorrectes, lorsqu’un élément LowCardinality non nullable d’une colonne Nullable(Tuple(...)) est utilisé comme sous-colonne (par exemple comme clé de tri). #108679 (groeneai).
  • Correction d’un crash dans la fonction Base64Decode du SDK Azure intégré, déclenchable via azureBlobStorage() (et les fonctions de table/stockages Azure associés) lorsque la clé du compte n’est pas un Base64 valide. Un octet >= 0x80 provoquait une lecture hors limites de la table de décodage, et tout octet invalide entraînait un décalage à gauche indéfini d’une valeur négative (arrêt brutal sous sanitizers). Les clés invalides sont désormais rejetées proprement. #108716 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (“New empty part is about to materialize but the directory already exist”) susceptible d’arrêter brutalement le serveur dans les builds de débogage et avec sanitizer lorsqu’une opération DROP/DETACH/MOVE/REPLACE PARTITION sur une table MergeTree s’exécutait après l’interruption d’une opération similaire antérieure (par exemple une transaction annulée ou un crash) ayant laissé derrière elle un répertoire obsolète tmp_empty_<part>. Ce répertoire obsolète est désormais réutilisé au lieu de provoquer un échec. #108879 (groeneai).
  • Correction d’une projection qui renvoyait la valeur par défaut du type d’une colonne (par ex. 0) au lieu de son DEFAULT défini dans le DDL (par ex. -1) après l’expiration du TTL de la colonne sur une part au format large. #109013 (tiandiwonder).
  • Correction de l’échec des requêtes ALTER sur les tables MergeTree créées avec un paramètre disk personnalisé (SETTINGS disk = disk(...)). #109199 (Michicosun).
  • Correction de l’apparition de lignes obsolètes et supprimées lors de la lecture des tables d’une base de données MaterializedPostgreSQL via une table Merge. #109339 (vdimir).
  • Correction de la lecture des tables Paimon partitionnées par une colonne BIGINT dont la valeur ne tient pas dans un Int32. Ces valeurs de partition étaient tronquées (par exemple, 9223372036854775807 devenait -1), ce qui produisait un chemin de partition erroné et entraînait une erreur du système de fichiers. #109510 (groeneai).
  • Correction d’une erreur logique (“Cannot determine row level filter; 0 columns deleted, 0 columns added”) lors de la lecture d’une table Merge dont une table enfant possède une politique de ligne dont le filtre se réduit à une colonne existante (par exemple, CREATE ROW POLICY ... USING flag). #109843 (groeneai).
  • IcebergLocal indiquait à tort Azure comme type de stockage. #109872 (PedroTadim).
  • CHECK TABLE signale désormais la corruption réelle d’une part de projection dont le chargement des métadonnées a échoué, au lieu d’une erreur trompeuse Columns doesn't match ... Expected: 0 columns. #110262 (Algunenano).
  • Correction de l’échec du démarrage du serveur pour les tables MergeTree sur des disques de stockage d’objet lorsqu’un fichier résiduel txn_version.txt.tmp présente des métadonnées corrompues au niveau du disque. #110519 (alexey-milovidov).
  • Correction d’un problème qui empêchait définitivement de rattacher une table MergeTree comportant une projection _part_offset ou d’ordre de commit lorsque allow_part_offset_column_in_projections ou allow_commit_order_projection avait été désactivé, puis que la table avait été détachée ou le serveur redémarré. Ces deux paramètres ne s’appliquent qu’au moment du CREATE et ne sont donc plus revérifiés lors de l’ATTACH. #110570 (groeneai).
  • Correction d’une LOGICAL_ERROR rare (“Unexpected number of parts to remove from parts_queue”) susceptible d’interrompre le serveur sur une table ReplicatedMergeTree lorsque deux opérations sur des partitions (par exemple, deux requêtes MOVE PARTITION / REPLACE PARTITION, ou l’une d’elles en concurrence avec un DROP_RANGE en arrière-plan) annulaient simultanément la vérification en arrière-plan des parts sur des plages qui se chevauchent. #110738 (groeneai).
  • Correction d’une erreur logique Hash is not set for serialization pouvant survenir lorsqu’une colonne utilisant un codec Quantized(...) était imbriquée dans une sérialisation poolable, par exemple une table Merge portant sur des sources dont la colonne de même nom a des types différents (fusionnés en un Variant). #110776 (groeneai).
  • Correction d’une std::length_error (LOGICAL_ERROR) lors de la lecture d’une table MergeTree avec une valeur aberrante de max_streams_for_merge_tree_reading (proche du maximum de UInt64) combinée à allow_asynchronous_read_from_io_pool_for_merge_tree = 1. Ce paramètre est désormais plafonné à la même limite que max_threads. #110936 (groeneai).
  • Correction d’une erreur logique Cannot calculate columns sizes when columns or checksums are not initialized lors de la fusion d’une table MergeTree à colonne unique dont la seule colonne possède un TTL de colonne expirant entièrement (avec min_bytes_for_wide_part = 1). #111169 (groeneai).
  • Les fichiers d’index de texte d’une part fusionnée ou matérialisée sont désormais synchronisés via fsync lorsque min_rows_to_fsync_after_merge / min_compressed_bytes_to_fsync_after_merge / fsync_part_directory sont définis. Auparavant, ces fichiers étaient les seuls de la part à ne pas être synchronisés : une coupure de courant juste après une fusion pouvait donc laisser une part validée mais corrompue et entraîner une perte de données. #111335 (groeneai).
  • Correction de OPTIMIZE TABLE ... MANIFEST pour les tables Iceberg lorsque les tuples de partition du manifest ne correspondent pas à leur spécification de partition #111368 (SmitaRKulkarni).
  • Correction d’une lecture hors limites (en release) / erreur logique (en debug) dans l’optimisation de matérialisation paresseuse, ainsi que d’une erreur logique dans Pipe::unitePipes pour les tables Merge, lorsque l’optimisation de lecture directe depuis l’index de texte laisse une colonne virtuelle d’index inutilisée dans l’en-tête de lecture d’une table MergeTree comportant plusieurs index text. #111467 (groeneai).
  • Correction d’une LOGICAL_ERROR (“Part level Min-Max index was constructed from unexpected columns set”) susceptible d’interrompre une fusion en arrière-plan dans les constructions debug/sanitizer après l’abaissement de part_minmax_index_columns (par exemple de with_block_number_offset à partition_key_only). #111511 (groeneai).
  • Correction d’un plantage lors de l’exécution d’une lecture STREAM (enable_streaming_queries = 1) sur une table comportant plus d’une partition et dotée d’une politique de lignes. Dans les constructions debug et sanitizer, l’opération échouait avec l’erreur logique Filter column ... not found in DAG outputs ; dans les constructions release, elle pouvait provoquer une erreur de segmentation. #111572 (groeneai).
  • Correction d’une LOGICAL_ERROR (Cannot create part with type Compact and storage type Full because table does not support polymorphic parts) levée lorsqu’une table MergeTree charge une part Compact préexistante alors que ses paramètres actuels désactivent les parts polymorphes (granularité non adaptative). Cela pouvait se produire, par exemple, lorsqu’une table en lecture seule partage un chemin sur disque avec une autre table et recharge ses parts via SYSTEM RESTART DISK ou au démarrage du serveur. Dans les constructions debug et sanitizer, l’exception provoque l’arrêt brutal du serveur. #111626 (groeneai).
  • Avec fsync_part_directory = 1, le renommage d’un répertoire de part de son nom temporaire vers son nom définitif est désormais lui-même rendu durable par un fsync du répertoire parent (et, pour les déplacements entre parents différents, également du parent source). Auparavant, seul le répertoire déplacé était synchronisé : une coupure de courant juste après la validation d’une part pouvait donc faire disparaître cette part du disque alors que ses fichiers étaient déjà écrits, entraînant la perte de lignes malgré l’activation du paramètre. #111861 (groeneai).
  • Correction de résultats erronés et d’une LOGICAL_ERROR lorsqu’une colonne Nullable(Tuple(...)) est sélectionnée en même temps que l’une de ses sous-colonnes d’élément pouvant contenir des NULL (Variant, Dynamic ou LowCardinality) depuis une table MergeTree. La lecture de la sous-colonne marquait les lignes NULL dans le tuple parent comme NULL dans une colonne déjà partagée via le cache des sous-flux, si bien que la lecture de la colonne entière renvoyait des valeurs décalées. #111949 (groeneai).
  • Correction des jointures par hachage qui perdaient leurs optimisations post-construction (conversion en table de hachage fixe, runtime filter partagé, réordonnancement de la table de droite) dès qu’un seuil de déversement automatique sur disque était configuré, ce qui est le cas par défaut depuis la version 26.5. #111972 (alexey-milovidov).
  • Correction de résultats erronés (lignes manquantes sans avertissement) pour hasAllTokens et les autres recherches multi-jetons sur l’index text dans les builds qui n’utilisent pas simdcomp (toutes les cibles sauf Linux x86_64, par exemple aarch64) lorsque text_index_posting_list_apply_mode = 'lazy'. Le décodeur de bitpacking portable n’écrivait pas les zéros décodés pour un bloc dont la largeur en bits est nulle, si bien qu’une entrée de liste de postings située dans un segment à entrée unique était ignorée. Le format sur disque reste inchangé et les données existantes sont correctement lues après la correction. #112178 (groeneai).
  • Les opérations échouent désormais lors du chargement de statistiques MergeTree corrompues, au lieu d’ignorer l’erreur. #112201 (skuznetsov-clickhouse).
  • Correction de la perte silencieuse d’écritures Iceberg acquittées sur HDFS : un commit Iceberg publie chaque nouveau fichier de métadonnées au moyen d’une écriture conditionnelle, mais HDFSObjectStorage ignorait la condition et effectuait un simple écrasement, de sorte que tous les rédacteurs concurrents étaient acquittés alors que l’instantané de l’un d’eux devenait inaccessible. HDFS refuse désormais les écritures conditionnelles ; les écritures Iceberg gérées par le stockage sur HDFS, y compris CREATE TABLE ... ENGINE = IcebergHDFS(...) avec une liste de colonnes explicite, échouent donc désormais. Les lectures ne sont pas affectées. #112437 (groeneai).
  • Correction de DELETE sur les tables KeeperMap, qui ne supprimait que les max_block_size premières lignes correspondantes tout en signalant la mutation comme terminée avec succès. Correction également du manque d’atomicité de DELETE avec keeper_map_strict_mode = 1 : l’opération pouvait basculer vers des suppressions non versionnées et supprimer des lignes mises à jour simultanément, et elle appliquait les lignes correspondantes bloc par bloc, si bien qu’un conflit pouvait laisser la suppression partiellement appliquée. #112777 (alexey-milovidov).
  • Correction d’une erreur transitoire d’Azure Blob Storage sur le chemin de lecture ou de fusion — un 403 Forbidden lié à la propagation des autorisations RBAC, un échec d’authentification des identifiants ou un délai de connexion dépassé remonté sous forme de 408 — qui était classée à tort comme ne pouvant pas donner lieu à un réessai et signalée comme POTENTIALLY_BROKEN_DATA_PART (Code 740), déclenchant une fausse alerte critique pour une partie de données pourtant saine. Ces échecs transitoires sont désormais considérés comme pouvant donner lieu à un réessai, et un échec réellement persistant remonte sous la forme d’une simple erreur Azure plutôt que d’une partie de données prétendument endommagée. #112871 (arsenmuk).
  • Correction de listObjects sur un disque Azure Blob Storage avec endpoint préfixé, qui renvoyait des noms de blob erronés sur toutes les pages suivant la première, car la pagination contournait le wrapper client chargé de retirer le préfixe de l’endpoint. La pagination repasse désormais par le wrapper via le jeton de continuation, de sorte que les noms de blob sont corrects sur chaque page. #112872 (arsenmuk).
  • Correction de la perte, après un rechargement de la table, de l’index minmax par partie de données sur _block_number et _block_offset lorsque la partie avait été produite par une mutation qui ne réécrit pas la partie entière. #112878 (alexey-milovidov).
  • Correction d’une exception (dans les builds de production, une erreur empêchant le démarrage du serveur) lorsqu’un prédicat de mutation utilise la fonction de table merge sans nom de base de données explicite. La base de données de la table mutée est désormais utilisée, comme c’est déjà le cas pour les noms de table. #113185 (alexey-milovidov).
  • Correction de résultats erronés et d’une erreur LOGICAL_ERROR lors de la lecture d’une sous-colonne d’un membre Nested supprimé puis rajouté, conjointement avec un membre présent du même groupe, sur une table MergeTree avec share_nested_offsets = 1. Les membres présents renvoyaient des valeurs provenant d’un bloc ultérieur dès que la lecture couvrait plus d’un bloc. #113225 (groeneai).
  • Correction d’une exception arithmétique dans les builds de débogage et avec sanitizers lors de la lecture d’une table Merge avec une valeur très élevée de max_streams_to_max_threads_ratio : le nombre de flux était tronqué à 32 bits, de sorte que les multiples de 2^32 devenaient nuls. Une erreur PARAMETER_OUT_OF_BOUND est désormais levée au lieu de provoquer un comportement indéfini lorsque la multiplication du nombre de flux pour une table Merge dépasse size_t. Un dépassement est également évité lors de la limitation du nombre de flux MergeTree en fonction du volume de données, et toute lecture de table Merge dont le nombre total de sources dépasserait 65536 est désormais rejetée. #113382 (alexey-milovidov).
  • Correction des écritures sur le disque hdfs : elles échouaient avec Parent directory doesn't exist, car les clés d’objet générées contiennent un préfixe de répertoire imbriqué ; or, contrairement aux stockages blob, HDFS possède de véritables répertoires et ne crée pas le répertoire parent manquant lors de la création d’un fichier. #113396 (alexey-milovidov).
  • Correction de l’erreur signalée lorsque les fonctions de table remote/remoteSecure ou les moteurs de table Remote/RemoteSecure reçoivent une collection nommée inexistante accompagnée d’une surcharge key = value. Un tel appel était silencieusement réanalysé comme un appel positionnel : la surcharge était alors interprétée comme une expression de données, et l’erreur signalée portait sur un élément sans rapport (par exemple Function with name merge does not exist pour remote(collection, database = merge(...))). C’est désormais l’absence de la collection nommée qui est signalée. #113510 (groeneai).
  • Correction d’une boucle infinie dans les fusions en arrière-plan et OPTIMIZE lorsque merge_max_block_size_bytes était défini en dessous du byteSize() d’une colonne vide. Une colonne LowCardinality vide contient toujours son dictionnaire : la limite en octets pouvait donc être atteinte avant même la fusion d’une seule ligne ; la fusion produisait alors indéfiniment des blocs vides, consommant 100 % d’un cœur CPU sans jamais libérer son emplacement dans le pool d’arrière-plan. #113585 (groeneai).
  • Correction d’une mutation d’une part Compact qui produisait un résultat différent selon que les informations de sérialisation de la part étaient encore en mémoire ou avaient été rechargées depuis serialization.json, lorsque la table utilise serialization_info_version = 'basic'. Avec ReplicatedMergeTree, deux répliques détenant une part source identique octet par octet écrivaient ainsi des parts mutées différentes, et la mutation échouait avec CHECKSUM_DOESNT_MATCH. #113588 (groeneai).
  • Les sauvegardes signalent désormais l’erreur sous-jacente d’accès au fichier de verrou, au lieu d’indiquer à tort une sauvegarde concurrente, lorsque le stockage de destination ne parvient pas à lire le fichier .lock. #113637 (kirillshokhin).
  • Correction d’une condition de concurrence entre les tâches en arrière-plan d’une table MergeTree (rafraîchissement des statistiques, rafraîchissement des parts, chargement des parts obsolètes/inattendues) et la destruction de la table. #113722 (alexey-milovidov).
  • Correction de UPDATE et du DELETE léger, qui lisaient les mauvaises tables lorsque la base de données de la table mise à jour différait de celle de la session. L’expression de la mutation est désormais canonicalisée par rapport à la base de données de la table mise à jour, aussi bien pour la fonction de table merge, qui utilise implicitement la base de données courante, que pour les identifiants de table non qualifiés. Auparavant, ces noms étaient résolus par rapport à la base de données de la session, si bien que l’instruction mettait à jour ou supprimait silencieusement des lignes sélectionnées dans une autre table. #113748 (groeneai).
  • Correction d’une exception lors de la lecture d’une table MergeTree par couches de plages de clé primaire lorsqu’une couche produit un pipe vide. #114177 (alexey-milovidov).
  • BACKUP, RESTORE et ENGINE = Backup vérifient désormais l’emplacement de la sauvegarde par rapport aux privilèges SOURCES de l’utilisateur, comme le font s3(), file() et azureBlobStorage(). Auparavant, un utilisateur disposant de BACKUP mais pas de SOURCES pouvait écrire une sauvegarde vers n’importe quel emplacement accessible au serveur, ou en lire une depuis cet emplacement. L’écriture d’une sauvegarde nécessite désormais la direction WRITE sur la source de la destination (WRITE ON S3, WRITE ON AZURE, WRITE ON FILE) et sa lecture nécessite READ ; les destinations Disk(...) restent limitées uniquement par backups.allowed_disk. #114405 (groeneai).
  • Correction de la lecture d’une table pendant qu’une mutation est en cours : une colonne MATERIALIZED pouvait renvoyer une valeur obsolète, et la sélectionner conjointement avec une colonne mise à jour pouvait provoquer une erreur. #114561 (tiandiwonder).
  • Correction d’une erreur logique (Block structure mismatch) lors de la lecture d’une table Merge avec make_distributed_plan activé, lorsque les tables enfants comportent des opérations ensemblistes internes. #114753 (alexey-milovidov).
  • OPTIMIZE TABLE sur une table de stockage objet dont le moteur n’implémente pas la compaction signalait un succès sans rien faire. Une erreur est désormais levée, comme pour tout autre moteur ne prenant pas en charge cette instruction. Cela concerne DeltaLake, Paimon, Hudi et les moteurs de stockage objet simples (S3, GCS, COSN, OSS, AzureBlobStorage, HDFS et URL adossé à un stockage objet) ; Iceberg, qui implémente bien la compaction, n’est pas concerné. #114805 (groeneai).
  • Correction de l’erreur BAD_DATA_PART_NAME (« Trying to get part name in new format for old format version ») sur une table ReplicatedMergeTree créée avec la syntaxe positionnelle obsolète via allow_deprecated_syntax_for_merge_tree. Une mutation pouvait être signalée comme terminée sur une réplique qui ne l’avait pas appliquée, et la table pouvait rester en lecture seule après un redémarrage. #114980 (groeneai).
  • Correction de l’erreur Found patch part ... that intersects mutation with version ... (LOGICAL_ERROR) sur les tables utilisant la mise à jour légère. Une fusion de patch parts pouvait produire un patch dont la plage de versions de données englobait la version de données d’une partie de données existante ; ce patch ne pouvait alors être ni appliqué ni ignoré, si bien que toute opération ultérieure sur la partition échouait et que la file d’attente de réplication se bloquait. De telles fusions ne sont plus planifiées. #115064 (alexey-milovidov).
  • Correction de l’échec du démarrage du serveur lorsqu’un moteur de base de données Backup fait référence à une sauvegarde devenue indisponible (par exemple, lorsque ses fichiers ont été supprimés ou que le stockage sous-jacent est inaccessible). La base de données est désormais chargée sans tables au lieu d’empêcher le démarrage de l’ensemble du serveur. #83188 (orloffv).
  • Correction de la reprise de la journalisation après une erreur de disque plein. Auparavant, lorsque le disque des journaux était plein, ClickHouse passait dans un état d’échec et inondait continuellement syslog de messages d’erreur (jusqu’à plus de 100 Go par heure), sans jamais se rétablir, même après la libération d’espace disque. Désormais, le système de journalisation se rétablit automatiquement dès que de l’espace disque redevient disponible, sans qu’un redémarrage du serveur soit nécessaire. #93127 (jaehanbyun).
  • Correction d’un plantage dans le visiteur de schéma du stockage DeltaLake. #97112 (kssenii).

Corrections relatives aux lacs de données et au stockage externe

  • Correction d’un échec transitoire Code: 499 ... InvalidPart (S3_ERROR) lors des téléversements multipart S3 (par exemple INSERT INTO ... hits_s3). MinIO peut brièvement signaler comme manquante une partie de données tout juste téléversée lors de CompleteMultipartUploadᅠ; cette erreur donne désormais lieu à de nouvelles tentatives, comme l’erreur NoSuchKey déjà prise en charge, dans la limite fixée par s3_max_unexpected_write_error_retries. #109364 (groeneai).
  • Les requêtes ALTER TABLE ... DELETE et ALTER TABLE ... UPDATE sur les tables Iceberg dont le format de fichier de données n’est pas Parquet sont désormais rejetées avec une erreur explicite NOT_IMPLEMENTED, au lieu de faire planter le serveur ou de corrompre silencieusement la table. #105893 (groeneai).
  • Correction de l’erreur Cannot add column ...: column with this name already exists lors d’un INSERT SELECT depuis une fonction de table telle que file, s3 ou input, lorsque la même colonne source est sélectionnée plusieurs fois. #105982 (crakjie).
  • Les valeurs sensibles transmises comme paramètres de chaîne de requête HTTP (par exemple via la liaison de paramètres de requête param_*, comme param_secret_key/param_aws_secret_access_key, le paramètre password ou les paramètres signature de type S3) ne sont plus écrites en clair dans system.text_log ni dans les spans OpenTelemetry. La ligne de journal Request URI masque désormais les valeurs des paramètres dont le nom semble sensible, en les remplaçant par [HIDDEN]. #108475 (groeneai).
  • Correction de la colonne virtuelle _etag pour les moteurs de table S3Queue et AzureQueueᅠ: elle était déclarée mais jamais renseignée, si bien que SELECT _etag renvoyait toujours une chaîne vide. Elle renvoie désormais l’ETag de l’objet, comme le moteur S3 et la fonction de table s3(). #108625 (groeneai).
  • Une requête de lecture ou d’écriture sur S3 annulée (par exemple via KILL QUERY) pendant qu’une requête S3 est en cours est désormais signalée comme annulée, et non plus par une erreur réseau/S3 trompeuse. En particulier, une sauvegarde ou une restauration vers S3 annulée indique désormais BACKUP_CANCELLED/RESTORE_CANCELLED dans system.backups. #108673 (jkartseva).
  • Correction de la colonne virtuelle _tags pour le moteur de table S3Queueᅠ: elle était déclarée mais jamais renseignée, si bien que SELECT _tags renvoyait toujours une map vide. Elle renvoie désormais les balises de l’objet, comme le moteur S3 et la fonction de table s3(). #108676 (groeneai).
  • Correction de SHOW TABLES et SELECT … FROM system.tables, qui renvoyaient des résultats différents pour les bases de données de catalogue de data lakeᅠ: les tables que ClickHouse ne peut pas lire sont désormais masquées de manière cohérente dans les deux cas. #109273 (SmitaRKulkarni).
  • Correction d’un arrêt brutal du serveur (ou, dans les builds release, d’un fichier de données silencieusement non finalisé) lorsqu’un INSERT dans une table Delta Lake (DeltaLakeLocal / deltaLake) échoue alors que le sink a déjà commencé à écrire, par exemple lorsque la requête source lève une exception en cours d’insertion ou que la requête est annulée. #109328 (groeneai).
  • Correction de Not found column or subcolumn c.null in block (et de Bad cast from ColumnConst to ColumnNullable dans les builds debug/sanitizer) lors de l’exécution de WHERE col IS NULL / IS NOT NULL sur une colonne Iceberg ajoutée par évolution de schéma après l’écriture de fichiers de données plus anciens, avec optimize_functions_to_subcolumns=1 (valeur par défaut). #109515 (groeneai).
  • Correction de l’erreur Host is empty in S3 URI lorsqu’une sous-requête scalaire est utilisée comme argument URL de la fonction de table s3 (ou comme argument de toute autre fonction de table) dans des requêtes CREATE TABLE ... AS SELECT : les sous-requêtes scalaires figurant dans les arguments des fonctions de table sont désormais évaluées lors de l’analyse de requêtes. #110254 (CheSema).
  • Correction d’une erreur logique (arrêt du serveur dans les builds debug/sanitizer) lorsqu’un ALTER multi-commandes tel que UPDATE ..., DELETE ... était exécuté sur une table Iceberg. Ce type de mutation est désormais rejeté avec une erreur NOT_IMPLEMENTED explicite. #110347 (groeneai).
  • Correction d’un problème où les tables Iceberg produisaient des ID de champ en double après ALTER TABLE ... ADD COLUMN lorsque le schéma initial contenait des champs imbriqués (Tuple/Array/Map). last-column-id enregistre désormais l’ID de champ maximal attribué, enfants imbriqués compris : une colonne ajoutée ultérieurement ne réutilise donc plus l’ID d’un champ imbriqué, et les lectures n’échouent plus avec ICEBERG_SPECIFICATION_VIOLATION (Duplicate field id). #110884 (groeneai).
  • Correction d’un problème où DELETE/UPDATE sur une table Iceberg supprimait silencieusement les mauvaises lignes (ou levait NOT_FOUND_COLUMN_IN_BLOCK) lorsque la table contenait des fichiers de données écrits à la fois avant et après ALTER TABLE ... ADD COLUMN et que optimize_move_to_prewhere était activé. #111693 (groeneai).
  • Correction de made_current_at dans system.iceberg_history, qui prenait la valeur 1970-01-01 pour un snapshot conservé après ALTER TABLE ... EXECUTE expire_snapshots sur une table Iceberg. Correction également de system.iceberg_history, qui ne renvoyait aucune ligne pour une table dont les métadonnées ne contiennent pas de snapshot-log. #111781 (groeneai).
  • Correction d’une corruption de données lors de l’insertion dans une table Iceberg via une MATERIALIZED VIEW. Le fichier de données était écrit selon les types de colonnes de la cible définis au moment du CREATE, et non selon son schéma Iceberg actuel : une valeur DateTime était ainsi stockée comme un simple int Avro et relue comme des microsecondes, tandis qu’avec DateTime64(0) ou DateTime64(9), le fichier devenait totalement illisible. En conséquence, INSERT INTO <materialized view> vers une cible dont le schéma Iceberg est plus large que les colonnes déclarées de la vue lève désormais TYPE_MISMATCH au lieu d’écrire une ligne corrompue. #112359 (groeneai).
  • Correction de l’écriture des tables Iceberg dont le schéma contient un tuple imbriqué dans un autre tuple. Les métadonnées de table publiées remplaçaient les noms de champs du tuple interne par des noms positionnels 1, 2, … : avec le format par défaut Parquet, l’INSERT échouait avec INCORRECT_DATA, et avec Avro, la colonne ne pouvait pas être relue. #112480 (groeneai).
  • Correction d’un interblocage qui bloquait définitivement une base de données Filesystem ou HDFS lorsque deux requêtes résolvaient simultanément le même nom de table. Toute requête ultérieure sur cette base de données restait alors bloquée indéfiniment, sans pouvoir être interrompue, et seul un redémarrage du serveur permettait d’en sortir. #112495 (groeneai).
  • Correction d’un problème où les tables DeltaLake renvoyaient trop peu de lignes lorsque le prédicat WHERE contenait, sous un NOT, une sous-expression que le traducteur de prédicats de delta-kernel ne sait pas traiter. Une telle sous-expression est désormais signalée à delta-kernel comme un prédicat inconnu explicite, et non plus comme la fin de l’itérateur enfant, ce qui avait pour effet de tronquer silencieusement la conjonction englobante. #112652 (groeneai).
  • Correction de ALTER TABLE ... EXECUTE remove_orphan_files, qui supprimait un fichier metadata/version-hint.text actif d’une table Iceberg. Son entrée dans l’ensemble des fichiers joignables était construite par concaténation de strings en supposant une barre oblique finale sur une valeur qui n’en comporte jamais : le véritable fichier d’indice n’était donc jamais reconnu comme joignable et était supprimé dès qu’il dépassait le seuil older_than. Avec iceberg_use_version_hint = 1, la table devenait alors totalement illisible. #113548 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR lors du filtrage d’une colonne Iceberg rendue Nullable par ALTER TABLE ... MODIFY COLUMN. #114521 (groeneai).
  • Correction d’un cas où ATTACH TABLE avec une définition ENGINE = URL(...) ne vérifiait pas le privilège TABLE ENGINE du moteur vers lequel le schéma d’URL route la requête. Un utilisateur ne disposant que de TABLE ENGINE ON URL pouvait rattacher puis lire une table reposant sur File, S3, AzureBlobStorage ou HDFS, ce que CREATE TABLE refuse à juste titre. #115914 (groeneai).
  • Correction des requêtes HTTP sortantes (par ex. `aiGenerate`, la fonction de table `url`, S3) qui échouaient avec `No route to host` sur les hôtes annonçant à la fois des adresses IPv4 et IPv6 lorsqu’une seule famille d’adresses est routable. Le pool de connexions HTTP bascule désormais sur l’adresse résolue suivante lorsque la première échoue en raison d’une erreur réseau, au lieu de propager l’erreur dès la toute première requête. #103786 (alexey-milovidov).
  • Possibilité de lire un array Avro d’enregistrements {key, value} à deux champs dans une colonne ClickHouse Map. Il s’agit de l’encodage produit par Iceberg et Spark pour un MAP<K, V> dont la clé n’est pas une string (les maps natives Avro n’acceptent que des clés de type string). Auparavant, un tel fichier pouvait être lu en tant que Array(Tuple(key, value)), mais pas en tant que Map(K, V), ce qui échouait avec l’erreur Type Map(...) is not compatible with Avro array. #109289 (groeneai).
  • Correction de la lecture d’une table Iceberg dont un fichier de métadonnées porte un numéro de version entièrement numérique mais dépassant la plage des entiers 32 bits (par exemple v99999999999999999999.metadata.json). Un tel nom produisait auparavant une erreur opaque std::out_of_range (STD_EXCEPTION) au lieu d’une erreur explicite BAD_ARGUMENTS. #109619 (groeneai).
  • Correction du nom des fichiers de métadonnées Iceberg compressés avec gzip. ClickHouse les écrivait sous la forme v{N}.gzip.metadata.json (le jeton HTTP Content-Encoding), alors que la spécification Iceberg attend l’extension gz, soit v{N}.gz.metadata.json. Par conséquent, Spark et les autres lecteurs de catalogue Hadoop ne trouvaient pas les métadonnées écrites par ClickHouse. ClickHouse écrit désormais v{N}.gz.metadata.json et continue de lire l’ancien nom gzip pour assurer la rétrocompatibilité. #109812 (groeneai).
  • Correction d’une table Iceberg au format d’écriture Avro qui sérialisait comme obligatoire un champ déclaré "required": false de type list, map ou struct, sans union ["null", T] : le schéma propre au fichier de données divergeait alors des métadonnées de la table, et les autres lecteurs Iceberg voyaient un champ obligatoire. Un tel champ est désormais écrit sous la forme de l’union ["null", T] que la spécification Iceberg utilise pour un champ facultatif. #112648 (groeneai).
  • Correction de made_current_at dans system.iceberg_history, qui indiquait le premier instant où un snapshot est devenu courant au lieu du dernier, lorsque le même snapshot-id apparaît plusieurs fois dans le snapshot-log d’Iceberg (comme c’est le cas après un rollback). #113598 (groeneai).
  • Correction d’un blocage pouvant durer jusqu’à une heure dans BACKUP/RESTORE ... TO S3, les disques GCS et la fonction de table s3 lorsque la collection nommée définit http_client = 'gcp_oauth' et que l’endpoint de jeton OAuth2 de Google accepte la connexion sans jamais répondre. La requête d’obtention du Bearer token n’hérite plus du délai d’expiration des requêtes de transfert de données de l’appelant ; l’attente de sa réponse est plafonnée à 10 secondes, comme pour la requête STS AssumeRole. #115531 (groeneai).
  • Correction d’une erreur logique dans le cache du système de fichiers (Expected file ... not to exist) qui pouvait survenir lorsqu’un téléchargement en arrière-plan vers le cache échouait avec une exception autre que ErrnoException (par exemple lors d’un OPTIMIZE sur une table Iceberg), laissant derrière lui un fichier de cache orphelin vide. #110549 (groeneai).
  • Correction des caches dimensionnés par un nombre maximal d’entrées (politique SLRU), qui cessaient silencieusement d’accepter de nouvelles entrées dès que ce nombre d’entrées avait été consulté au moins deux fois. Sont notamment concernés les caches de fichiers de métadonnées Iceberg et Paimon, le cache de métadonnées Parquet, les caches d’index de texte, le cache d’index de similarité vectorielle, le cache d’expressions compilées et le cache d’état des fichiers ObjectStorageQueue. #115517 (groeneai).
  • Correction des lectures de tables Paimon qui pouvaient échouer lorsque l’indice mutable snapshot/LATEST était remplacé de manière concurrente. En cas d’indice invalide ou obsolète, la lecture bascule désormais de manière sûre vers le listage des snapshots. #112010 (JiaQiTang98).
  • Remontée de l’exception sous-jacente réelle lorsqu’une archive zip ne peut pas être décompressée parce que le tampon d’entrée (par exemple un tampon de lecture S3 avec restricted_seek = true) refuse un déplacement (seek) ou une lecture demandés par minizip. Auparavant, l’erreur réelle était masquée par le message générique Couldn't unpack zip archive: Code = -100 / Couldn't open zip archive. #105103 (groeneai).
  • Correction de l’écriture Iceberg pour les métadonnées sans refs #106896 (AVMusorin).
  • Correction d’une exception lors de l’insertion dans une table Iceberg dont les métadonnées ont été créées par un moteur externe et omettent les tableaux facultatifs snapshots, metadata-log ou snapshot-log. Ces insertions aboutissent désormais au lieu d’échouer. #107473 (tiandiwonder).
  • Correction d’une erreur ReadBuffer is canceled. Can't read from it. qui pouvait survenir lorsqu’un téléversement vers S3 (par exemple une sauvegarde) était relancé après une erreur de lecture transitoire. #108573 (groeneai).
  • Correction d’une std::future_error (« The associated promise has been destructed prior to the associated state becoming ready ») qui pouvait survenir, et provoquer l’arrêt du serveur dans les builds debug/sanitizer, lorsque la planification de la tâche asynchrone finale de complétion d’un téléversement multipart S3/Azure échouait (par exemple en cas d’épuisement du pool de threads). C’est désormais l’erreur de planification réelle qui est signalée. #108730 (groeneai).
  • Correction de la lecture des timestamp ORC au-delà de l’année ~2262 dans une colonne DateTime64 dont la scale est inférieure à 9. Le lecteur ORC natif convertissait auparavant chaque timestamp via un intermédiaire fixe DateTime64(9), ce qui provoquait un dépassement de capacité de Int64 à l’échelle de la nanoseconde et rejetait ces valeurs avec VALUE_IS_OUT_OF_RANGE_OF_DATA_TYPE, même lorsque la scale DateTime64 demandée (par exemple DateTime64(6), telle que produite par Iceberg) permettait de représenter la valeur. Les timestamp sont désormais lus directement à la scale demandée, et date_time_overflow_behavior est respecté lorsque même la scale cible ne peut pas contenir la valeur. #109302 (groeneai).
  • La lecture d’une table Paimon dont le schéma contient un type imbriqué non pris en charge (par exemple un champ ROW) renvoie désormais une erreur BAD_ARGUMENTS explicite indiquant le type en cause, au lieu d’un simple DB::Exception. (OK) avec le code d’erreur 0 et sans message. #109762 (groeneai).
  • Correction de la lecture des tables Iceberg partitionnées plusieurs fois selon la même colonne source (par exemple PARTITIONED BY (hours(ts), ts)). Ces tables échouaient auparavant avec Cannot add column ...: column with this name already exists (ILLEGAL_COLUMN). #109895 (groeneai).
  • Les fichiers de données écrits par ClickHouse dans des tables Iceberg intègrent désormais les identifiants de champ Iceberg pour les formats Avro (field-id) et ORC (attributs de type iceberg.id/iceberg.required), et les colonnes ORC String sont écrites en tant que string ORC plutôt qu’en binaire. Auparavant, seul Parquet intégrait les identifiants de champ : les lecteurs conformes à la spécification (par ex. Spark/iceberg-java) renvoyaient donc silencieusement des NULL pour les lignes Avro écrites par ClickHouse après le renommage d’une colonne, et étaient totalement incapables de lire les fichiers ORC écrits par ClickHouse. #109994 (groeneai).
  • Correction d’une erreur logique ChunkInfoRowNumbers does not exist lors d’un OPTIMIZE TABLE sur une table Iceberg contenant un fichier de données dans un format autre que Parquet (par ex. ORC) plus récent que tous les fichiers de suppression par position. #110107 (alexey-milovidov).
  • Correction de la lecture de fichiers Parquet volumineux depuis des entrées zip STORED non compressées via s3 et file. #110495 (Gudauu).
  • L’échec du démarrage d’un pool de planification en arrière-plan (par exemple le pool iceberg, en cas d’épuisement du Global Thread pool) n’interrompt plus le serveur : l’erreur est désormais une exception récupérable. Par ailleurs, la lecture de system.background_schedule_pool ne crée plus ces pools comme effet de bord. #111209 (groeneai).
  • Correction de l’authentification DLF dans le REST catalog Paimon : la signature de requête était calculée une seule fois puis réutilisée, si bien que toutes les requêtes au catalogue après la première échouaient avec une erreur HTTP 401 et que SHOW TABLES renvoyait une liste vide. Correction également de la détection des erreurs HTTP 404 lors des vérifications d’existence de table. #112015 (JiaQiTang98).
  • Correction d’une LOGICAL_ERROR (partitions_count > 0) lors du partitionnement de fragments de données Iceberg vidés par des suppressions par position, par ex. lors du compactage d’une table Iceberg partitionnée contenant des fichiers de données entièrement supprimés. #112432 (PedroTadim).
  • Correction de la suppression d’objets par batch sur Azure, qui n’enregistrait aucun événement Delete dans system.blob_storage_log lorsque la requête de batch elle-même échouait, si bien que le batch entier n’était pas journalisé. Un événement Delete est désormais enregistré pour chaque objet en cas d’échec au niveau du batch. #112873 (arsenmuk).
  • Correction de la lecture des tables Paimon partitionnées par une colonne TIMESTAMP dont la précision est supérieure à la milliseconde, qui échouait avec l’erreur scale 6 is not supported, only support scale <= 3. #113401 (JiaQiTang98).
  • Correction d’un plantage lors de la lecture d’une table de data lake dont le schéma déclare une colonne au nom vide. Les métadonnées Iceberg malformées sont rejetées avec ICEBERG_SPECIFICATION_VIOLATION, tandis qu’un schéma fourni par un catalogue, Delta Lake ou Paimon est rejeté avec AMBIGUOUS_COLUMN_NAME. La vérification étant inconditionnelle, une table Iceberg qui se contente de conserver un ancien schéma inutilisé contenant un nom de champ vide devient elle aussi illisible, au lieu de provoquer un arrêt brutal dès la lecture de ce schéma. #114394 (groeneai).
  • La lecture d’une table Iceberg dont les métadonnées décrivent une évolution du schéma interdite par la spécification Iceberg ne provoque plus l’arrêt brutal du serveur. Quatre violations de la spécification dans IcebergSchemaProcessor étaient signalées en tant que LOGICAL_ERROR, traitée comme un échec d’assertion, ou aboutissaient sans contrôle à une assertion fatale ; elles lèvent désormais ICEBERG_SPECIFICATION_VIOLATION. #114518 (groeneai).
  • Correction de la colonne virtuelle _time des fonctions de table url/urlCluster, qui valait toujours NULL sur les builds basés sur musl en raison d’une analyse non portable de l’en-tête HTTP Last-Modified. #111619 (thevar1able).
  • Correction d’un problème empêchant le serveur de démarrer après la création d’une table avec CREATE TABLE ... AS url('http://host/**/', ...) alors que allow_experimental_url_wildcard_from_index_pages était activé. Lors du chargement des métadonnées d’une telle table, la vérification de la fonctionnalité expérimentale était réévaluée et échouait avec SUPPORT_IS_DISABLED, ce qui interrompait le démarrage. #115095 (groeneai).
  • Une table du moteur de base de données URL ne révèle plus l’existence d’un fichier local à un utilisateur ne disposant pas du grant de lecture de la source : ni EXISTS TABLE, qui ne requiert que le privilège SHOW TABLES, ni la résolution d’une table n’interrogent plus le système de fichiers avant que le grant ne soit confirmé. #113029 (alexey-milovidov).
  • Application de query_masking_rules aux messages ajoutés via Exception::addMessage, afin que les identifiants encodés dans l’URL figurant dans les suffixes (in file/uri ...) des erreurs de jdbc()/odbc() ne soient pas divulgués lorsque des règles de masquage sont configurées. #106916 (gaurav0107).

Corrections relatives aux formats et à l’ingestion

  • Les lectures sans schéma de time32/time64 Arrow (par ex. SELECT * FROM file(..., 'Arrow')) infèrent désormais Time64 au lieu de DateTime64. Auparavant, le type inféré était DateTime64. Tout traitement en aval qui s’appuie sur l’ancien type inféré cessera de fonctionner après la mise à niveau. Ajoute également la prise en charge de l’export de Time/Time64 vers Arrow, le type temporel Apache Arrow approprié étant choisi en fonction de la précision. #104316 (Maoyao233).
  • Correction de résultats potentiellement erronés ou de lectures hors limites lorsqu’un INSERT est annulé après une erreur survenue en cours de lot (par exemple dans les tables Buffer, les insertions asynchrones ou les moteurs Kafka/RabbitMQ/FileLog) alors que la réplication différée des colonnes (enable_lazy_columns_replication) est activée. #108935 (groeneai).
  • Correction d’une incohérence du parseur : la liste de colonnes d’un INSERT acceptait un sélecteur de colonnes qualifié écrit sous la forme t.* LIKE '<pattern>' / t.* ILIKE '<pattern>', mais rejetait sa forme canonique t.COLUMNS('<regexp>'), ce qui cassait l’aller-retour de formatage de la requête (et faisait planter le serveur dans les builds de débogage/sanitizer). #109176 (groeneai).
  • Correction de l’échec de INSERT dans une table DataLakeCatalog Microsoft Fabric / OneLake avec l’erreur IncorrectEndpointError (HTTP 400) : les écritures ADLS Gen2 (DFS) sont désormais acheminées vers l’hôte d’endpoint .dfs au lieu de l’hôte .blob. Remarque : avec remote_url_allow_hosts, les hôtes Fabric .blob (lecture) et .dfs (écriture) doivent tous deux figurer dans la liste d’autorisation pour INSERT. #110290 (zlareb1).
  • Correction des écritures via les fonctions de table de data lake (par ex. INSERT INTO FUNCTION icebergS3(...)) qui produisaient des fichiers Parquet dont les colonnes de type String perdaient l’annotation de type String, rendant les données écrites illisibles pour des lecteurs externes tels que Spark. Ces écritures utilisent désormais les mêmes paramètres de format que les écritures via le table engine. #110465 (tiandiwonder).
  • Correction de system.session_log, qui devenait illisible après une connexion Arrow Flight : la colonne interface est de type Enum8, mais la valeur ArrowFlight était absente de son énumération ; une connexion via le protocole Arrow Flight écrivait donc la valeur brute 10, et tout SELECT lisant une telle ligne levait l’exception Unexpected value 10 in enum. #110758 (alexey-milovidov).
  • Correction de l’inférence de schéma pour la famille de formats TSV, qui inférait String pour une valeur décimale écrite avec un zéro initial, comme 0.0 ou 0.5, alors que CSV infère correctement Float64. De plus, une première ligne contenant de telles valeurs était interprétée comme en-tête par la détection automatique d’en-tête de TSV et disparaissait du résultat. Corrige également TSKV, qui inférait un type numérique pour une valeur écrite à l’aide d’une séquence d’échappement, comme x=1\x2E5, dont l’analyse échouait ensuite. Les valeurs dont le zéro initial sert de remplissage pour un entier, comme 007, restent inférées comme String, car le parseur de valeurs TSV ne peut pas les relire. #112226 (groeneai).
  • Correction de l’inférence de schéma qui proposait un type numérique pour des valeurs textuelles que le parseur de valeurs ne peut pas relire. L’inférence acceptait un exposant incomplet (1e+), une mantisse manquante (.) ou un signe répété (++inf), que readFloatTextPrecise rejetait ensuite avec CANNOT_PARSE_NUMBER lors de la lecture du schéma inféré ; sur une colonne Dynamic, cela provoquait une erreur au moment de l’INSERT. L’inférence valide désormais le nombre avec le parseur réellement utilisé par le lecteur. Cela supprime également un cas où le type inféré dépendait de max_read_buffer_size, et permet aux formes d’exposant valides avec zéro initial, telles que 0e5, d’être inférées en Float64 en TSV, comme c’est déjà le cas en CSV. Les formes d’exposant mal formées sont également corrigées. #112453 (groeneai).
  • Correction de KILL QUERY et max_execution_time, qui pouvaient être ignorés pendant plusieurs minutes lorsqu’un format d’entrée orienté lignes analysait un bloc. Les formats d’entrée orientés lignes vérifient désormais l’annulation de la requête toutes les 8192 lignes : une insertion interrompue (en particulier un vidage d’insertion asynchrone en arrière-plan, qui analyse jusqu’à max_insert_block_size lignes par bloc) s’arrête donc rapidement au lieu de se poursuivre jusqu’à la fin du bloc. #112646 (groeneai).
  • Correction du type de colonne inféré qui dépendait de l’ordre des requêtes précédentes pour une source dont le schéma est mis en cache. Les paramètres modifiant un type inféré étaient absents de la clé du cache d’inférence de schéma, de sorte que la valeur de la première requête déterminait le type vu par toutes les requêtes suivantes : input_format_try_infer_exponent_floats, max_parser_depth, input_format_json_infer_array_of_dynamic_from_array_of_different_types, ainsi que, pour Parquet, input_format_parquet_local_time_as_utc, input_format_parquet_allow_geoparquet_parser, input_format_parquet_skip_columns_with_unsupported_types_in_schema_inference et schema_inference_make_json_columns_nullable. Enregistre également la fonction d’obtention de clé de cache manquante pour Form et fait en sorte que celle de Template utilise la règle d’échappement de son propre format de ligne. #113533 (groeneai).
  • Correction du formatage d’un argument de sous-requête des fonctions de table view et viewIfPermitted lorsque la requête englobante se termine par une clause SETTINGS. Une telle requête était formatée en view((SELECT ...)), forme impossible à réanalyser, si bien que la requête échouait à la vérification interne formatage-analyse-formatage et levait Inconsistent AST formatting. #114658 (groeneai).
  • Correction de données JSON détectées à tort comme TSKV lors de la détection automatique du format. #106009 (Avogar).
  • L’export vers le format Arrow de valeurs Time/Time64 ne correspondant pas à une heure de la journée valide (négatives ou >= 24h) est désormais rejeté avec une erreur explicite, au lieu de produire des données Arrow time32/time64 invalides. #107179 (tiandiwonder).
  • Correction de la lecture des fichiers Arrow, ArrowStream et ORC basés sur Arrow dont la colonne d’horodatage a pour fuseau horaire un décalage UTC numérique fixe (par ex. +05:30, -08:00, 00:00) ou le marqueur non IANA fixed, qui échouait auparavant avec Cannot load time zone ... (BAD_ARGUMENTS). #108633 (groeneai).
  • Sérialisation d’une colonne Nullable(Tuple(...)) en un seul champ CSV au lieu de l’aplatir en colonnes distinctes. Auparavant, une valeur non nulle était écrite sous forme de plusieurs champs CSV tandis qu’un NULL donnait un unique \N, ce qui produisait un nombre de colonnes variable selon la ligne et cassait les en-têtes CSVWithNames/CSVWithNamesAndTypes ainsi que les allers-retours. #108959 (groeneai).
  • Correction d’une allocation mémoire excessive lors de l’inférence de schéma du format MsgPack : une entrée corrompue ou générée par fuzzing dont l’en-tête array/map/string/binary déclare un nombre d’éléments gigantesque ne déclenche plus une allocation unique de plusieurs gigaoctets (elle est désormais rejetée comme entrée malformée). Cela corrige un arrêt allocation-size-too-big sous les sanitizers ainsi qu’une saturation de la mémoire dans les builds de production. #109019 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR lors de l’écriture d’une colonne LowCardinality(Time) au format Arrow avec output_format_arrow_low_cardinality_as_dictionary = 1. #109730 (groeneai).
  • Correction de la lecture d’un flux corrompu au format Native dont le nombre de types Dynamic ou le nombre de chemins JSON/Object est proche de SIZE_MAX : une telle entrée malformée est désormais rejetée avec une erreur INCORRECT_DATA explicite au lieu d’une exception std::length_error non interceptée. #110590 (alexey-milovidov).
  • Correction d’une erreur LOGICAL_ERROR: Cannot sum Bools (arrêt du serveur dans les builds debug/sanitizer) lors de la fusion d’états d’agrégation sumMap/sumMapWithOverflow/sumMapFiltered contenant des valeurs Bool sérialisées dans l’ancien format d’état (version 0). Cette correction résout également deux cas de résultats erronés silencieux avec des états Bool en version 0 : les clés de map de type Bool n’étaient pas dédupliquées entre les états, et le compactage des valeurs nulles supprimait les mauvaises entrées. #110922 (groeneai).
  • Correction de la fonction de table format, qui levait NOT_FOUND_COLUMN_IN_BLOCK lorsque l’analyse des données d’entrée ne produisait aucune ligne (par exemple un JSON vide, ou une FeatureCollection GeoJSON sans aucune feature). Elle renvoie désormais un résultat vide avec les bonnes colonnes. #111428 (groeneai).
  • Il est désormais possible de lire des fichiers GeoParquet, Arrow et ArrowStream dont la colonne de géométrie encodée en WKT utilise un mot-clé de type qui n’est pas entièrement en majuscules (point(1 2)) ou un conteneur vide (LINESTRING EMPTY, POLYGON(), POLYGON(())). L’importation de ces fichiers échouait auparavant avec BAD_ARGUMENTS ou CANNOT_PARSE_NUMBER, alors que ces mêmes chaînes sont correctement analysées par la fonction readWKT. Cela corrige également la relecture d’un fichier contenant la sortie de wkt pour une géométrie vide, qui s’écrit LINESTRING(). #112020 (groeneai).
  • Correction de l’erreur Unknown compression method levée pour les indications de compression auto et none lorsqu’elles n’étaient pas écrites entièrement en minuscules, par exemple file('data.csv.gz', 'CSV', 'x String', 'AUTO'). Les noms de codecs tels que GZIP étaient déjà acceptés sans tenir compte de la casse ; ces deux indications spéciales se comportent désormais de la même manière. #113631 (groeneai).
  • Correction de la lecture d’une colonne Tuple dont le premier élément est NULL dans les formats CustomSeparated, Regexp et Template avec la règle d’échappement CSV. Dans ces formats, un Tuple nu occupe un champ par élément ; le champ \N initial correspond donc à cet élément et non à la colonne entière. Auparavant, il était interprété comme la colonne entière, ce qui décalait les champs des éléments restants vers les colonnes suivantes : CustomSeparated ne pouvait donc pas relire sa propre sortie. #114471 (groeneai).
  • Correction du format d’entrée Regexp, qui ignorait silencieusement le reste non analysé d’un champ correspondant et stockait ainsi une valeur tronquée ou fabriquée au lieu de signaler une erreur : v=-1 dans UInt64 donnait 0, et v=2020-01-01junk dans Date donnait 1975-07-14 avec la règle JSON. Les champs mal formés sont désormais rejetés avec les règles Escaped, CSV et JSON, conformément au comportement documenté des formats correspondants. Avec Raw, un champ correspondant contenant une tabulation est désormais lu en entier au lieu d’être tronqué à la tabulation. #114520 (groeneai).
  • Correction d’une erreur logique Inconsistent AST formatting (arrêt du serveur dans les constructions debug et sanitizer) pour les expressions CREATE INDEX / CREATE HYPOTHETICAL INDEX qui ne résistaient pas à un aller-retour formatage-analyse-formatage, par exemple CREATE HYPOTHETICAL INDEX i0 ON t0 ((a())) TYPE a et CREATE INDEX i0 ON t0 ((a, b).1) TYPE minmax. #109175 (groeneai).
  • Correction d’une erreur logique (Logical error: 'KeyCondition uses PREWHERE output') dans le lecteur Parquet v3, qui survenait lorsqu’une condition de clé au niveau du format faisait référence à une colonne non lue depuis le fichier Parquet. #109267 (groeneai).
  • Correction d’une exception dans un gestionnaire HTTP prédéfini en cas d’en-tête absent #108324 (vitlibar).
  • Correction d’une possible erreur Logical error: 'ReadBuffer is canceled. Can't read from it.' lorsqu’un format d’entrée orienté lignes (par ex. TSV/CSV) ne parvenait pas à analyser l’entrée alors que la lecture sous-jacente avait déjà été annulée (par exemple en cas de chunk HTTP mal formé ou de corps tronqué). La construction des diagnostics d’analyse détaillés ne lit plus depuis un tampon annulé. #109708 (groeneai).
  • Le format Values n’enregistre plus d’erreurs d’analyse dans system.errors et system.error_log lorsque l’analyse en flux bascule avec succès vers l’analyse d’expressions SQL. #111141 (pamarcos).
  • Correction de SYSTEM REFRESH sur une table RabbitMQ arrêtée, qui ne parvenait parfois pas à consommer les messages en attente. Un REFRESH accorde exactement un cycle de streaming hors séquence, mais ce cycle pouvait se terminer immédiatement sans aucune ligne, car la boucle de livraison AMQP venait tout juste d’être démarrée de manière asynchrone : l’unique autorisation était alors gaspillée et les messages restaient non consommés jusqu’au prochain SYSTEM START. #111480 (groeneai).
  • Correction d’un problème où une table NATS avec nats_stream défini cessait silencieusement de consommer des messages après le redémarrage du serveur NATS. L’abonnement JetStream est désormais rétabli automatiquement, sans qu’il soit nécessaire d’exécuter DETACH TABLE puis ATTACH TABLE. #112828 (groeneai).
  • Rejet d’une valeur démesurée de kafka_num_consumers avec une erreur explicite, au lieu d’un échec d’allocation dans le table engine Kafka. Auparavant, avec kafka_disable_num_consumers_limit activé, une telle valeur provoquait une exception std::length_error. #114764 (alexey-milovidov).
  • Serveur Arrow Flight : acceptation des identifiants d’authentification Basic encodés en Base64 sans remplissage, tels qu’envoyés par les clients Flight écrits en Go comme le pilote ADBC Flight SQL, et renvoi d’une véritable erreur d’authentification au lieu de l’erreur générique Unexpected error in RPC handling lorsque l’en-tête authorization est mal formé. #115085 (alexey-milovidov).
  • Serveur Arrow Flight : rejet des identifiants d’authentification Basic mal formés dépourvus de séparateur entre le nom d’utilisateur et le mot de passe. #115347 (alexey-milovidov).

Corrections relatives aux types de données, aux fonctions et à la sérialisation

  • Correction d’une erreur logique Stream <col>.variant_discr ... is not found (arrêt du serveur dans les builds de débogage et avec sanitizers) lorsque ALTER TABLE ... MODIFY COLUMN convertit une colonne vers un type comportant des sous-colonnes dynamiques (par exemple Variant en Dynamic) sur une table avec des parts Wide, et que la part est ensuite lue ou fusionnée. #110204 (groeneai).
  • Correction de la lecture d’une sous-colonne d’une colonne Nullable(Tuple(...)) dont l’élément est un LowCardinality(T) non nullable, lorsque la même requête lit également la sous-colonne parente. Sur une part Compact, la sous-colonne parente renvoyait des valeurs erronées, la requête échouait avec Unexpected return type from assumeNotNull, ou encore le serveur était tué par une assertion de durcissement string_view, car la sous-colonne extraite était désérialisée dans un tampon LowCardinality(Nullable(T)) dont les sous-flux étaient ensuite partagés avec la lecture de la sous-colonne parente. #112232 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR « Not-ready Set is passed as the second argument for function globalNullIn » et de la perte du véritable message d’erreur de la requête, qui pouvaient survenir lorsque l’élagage des parts basé sur les statistiques exécutait de manière spéculative une sous-requête IN/GLOBAL IN pendant l’analyse d’index et que cette sous-requête échouait. #114121 (groeneai).
  • Correction de l’erreur Cannot read all data of type FixedString lors de la lecture de la sous-colonne product_quantization_codebook d’une colonne utilisant un codec Quantized('product', ...). Le dictionnaire de codes propre à chaque part étant écrit après les données de tous les granules, il n’est pas délimité par des marques : une lecture dont la plage de marques se terminait avant la dernière marque de la part pouvait donc le lire de façon incomplète. #112818 (alexey-milovidov).
  • Les parts dont la disposition Map contredit leurs propres métadonnées ne sont plus écrites, pour une valeur Map contenue dans une colonne Dynamic. Les tables ne différant que par map_serialization_version partageaient un même objet de sérialisation en cache : une part pouvait ainsi recevoir la disposition Map basic alors que ses métadonnées déclaraient with_buckets, et sa lecture ultérieure échouait avec LOGICAL_ERROR: Stream ...buckets_info ... is not found. Les parts déjà écrites de cette manière par une version antérieure restent illisibles et doivent être supprimées. #113514 (groeneai).
  • Correction de l’omission, par ALP, FPC et GCD, de leur paramètre dérivé du type dans le hachage du codec. Dans une part Compact, l’écrivain partage un flux compressé par hachage de codec distinct ; deux colonnes de largeurs de valeur différentes utilisant le même codec entraient donc en collision et partageaient un même objet codec portant une largeur erronée. Des insertions valides étaient alors rejetées avec CANNOT_COMPRESS lorsque les largeurs différaient, et l’une des colonnes était sinon moins bien compressée qu’elle n’aurait dû l’être. #114189 (groeneai).
  • Correction d’un élagage d’index incorrect lorsque la clé de tri d’une table encapsule une colonne Date dans toDateTime (par exemple ORDER BY toDateTime(date_column)) et qu’une requête filtre sur la colonne d’origine avec une comparaison telle que WHERE date_column >= '...'. toDateTime(Date) déborde pour les valeurs Date situées au-delà de la plage de DateTime (après le 2106-02-07) ; la clé stockée n’est donc pas monotone. ClickHouse n’utilise plus l’élagage par clé primaire pour cette combinaison clé/prédicat, car il risquerait d’écarter des granules contenant des lignes correspondantes. #101814 (nihalzp).
  • Correction de l’échec de ALTER MODIFY COLUMN lors de la conversion d’une colonne Nullable vers un autre type avec un DEFAULT (par exemple Nullable(UInt8) → String,LowCardinality(String) ou Array(UInt8)). #102156 (il9ue).
  • Correction de l’échappement incorrect des littéraux SQL dans StorageSQLite et la fonction de table sqlite() lors de la transmission des prédicats WHERE à SQLite : les guillemets simples et les caractères de contrôle (\n, \r, \t, \) étaient échappés avec des antislashs, que SQLite n’interprète pas, ce qui provoquait des erreurs de syntaxe ou des résultats de requête erronés. Correction également de l’échappement des littéraux de type string transmis à PostgreSQL : les strings imbriquées dans des listes IN conservaient l’échappement propre à ClickHouse, et les caractères de contrôle étaient envoyés sous forme de séquences d’antislash que PostgreSQL interprète comme des octets différents. #104217 (tiandiwonder).
  • Correction de l’exception NOT_FOUND_COLUMN_IN_BLOCK dans les requêtes INNER JOIN ... ON arrayJoin(...) = ... lorsque query_plan_convert_join_to_in est activé et que le SELECT référence la colonne du tableau source. #104809 (tiandiwonder).
  • Correction des expressions IN et NOT IN dont l’opérande de droite, non constant, référence des colonnes de la ligne en cours, et alignement du traitement des tuples en opérande de droite par le nouvel analyseur sur la sémantique IN existante de ClickHouse. Avec l’ancien analyseur, une colonne source seule en opérande de droite (x IN (arr)) est toujours résolue comme un nom de table et reste hors de portée. #104993 (niyue).
  • Correction des JOIN sur les tables EmbeddedRocksDB, Redis et KeeperMap, qui pouvaient renvoyer silencieusement zéro ligne lorsque DirectKeyValueJoin était utilisé avec des clés String ou Nullable(String). #105253 (vdimir).
  • Correction d’une erreur LOGICAL_ERROR (“Unexpected expression in JOIN ON section. Expected boolean (UInt8), got ‘Nothing’”) lorsqu’un prédicat JOIN ... ON de non-égalité référence une colonne de type Nothing, comme celle produite par ARRAY JOIN []. #106981 (groeneai).
  • Correction d’une erreur logique Inconsistent AST formatting pouvant faire planter le serveur dans les builds de débogage et avec sanitizers lorsqu’une lambda avec alias était utilisée comme opérande d’un opérateur d’accès (élément de tuple .N ou élément de tableau []) ailleurs qu’en première position d’une liste d’expressions, par exemple SELECT 1, ((p0, p1) -> p0 AS a7).4[3] FROM t. #107092 (groeneai).
  • Une fonction de table de cluster (urlCluster, fileCluster, s3Cluster, …) imbriquée dans une autre requête distribuée, comme clusterAllReplicas(..., urlCluster(...)), est désormais rejetée avec une erreur BAD_ARGUMENTS au lieu d’échouer avec une erreur logique (Distributed task iterator is not initialized). #107107 (groeneai).
  • Correction de l’échec de la fonction de table values avec ARGUMENT_OUT_OF_BOUND lors de l’utilisation d’un littéral décimal non représentable exactement dans une colonne à virgule flottante de faible précision (comme 0.1 pour une colonne Float32), par exemple SELECT * FROM values('x Float32', 0.1). Ces valeurs sont désormais acceptées et converties en la valeur représentable la plus proche, comme le font CAST et INSERT ... VALUES. #108055 (alexey-milovidov).
  • Correction de mapFilter, mapSort (et ses variantes) et mapConcat, qui supprimaient LowCardinality des types de clé et de valeur d’une Map. Auparavant, mapFilter appliqué à une colonne Map(LowCardinality(String), String) renvoyait Map(String, String), ce qui pouvait corrompre les métadonnées d’une table créée via CREATE TABLE ... AS SELECT et faire échouer CHECK TABLE. #108057 (alexey-milovidov).
  • Correction d’un INSERT annulé ou interrompu par KILL qui continuait à s’exécuter longtemps pendant la construction d’un index de saut (par exemple un index set(0) non borné sur une colonne à forte cardinalité). La construction de l’index s’arrête désormais rapidement lorsque la requête est annulée. #108351 (tiandiwonder).
  • Correction d’un SELECT sur la fonction de table primes qui ne réagissait pas à l’annulation : avec une valeur élevée de step (ou de limit), la requête pouvait continuer à s’exécuter longtemps après un KILL QUERY ou un dépassement de délai. Elle s’arrête désormais rapidement. #108353 (tiandiwonder).
  • Correction de l’analyse des noms de types de données contenant la sous-chaîne INT (par exemple un nom de type fonction tel que quantileInterpolatedWeighted utilisé à l’emplacement d’un type de données). Ces noms étaient pris pour des types entiers MySQL, et leur premier groupe d’arguments (...) était silencieusement consommé comme modificateur de largeur d’affichage, ce qui pouvait casser l’aller-retour de formatage de la requête. #108354 (groeneai).
  • Correction d’une incohérence : un ARRAY JOIN mal formé suivi d’une virgule et d’une liste de tables entre parenthèses était analysé à tort comme une jointure croisée au lieu d’être rejeté. #108365 (Algunenano).
  • Correction d’une exception LOGICAL_ERROR (“Invalid action query tree node …”) survenant lorsqu’une requête distribuée dédupliquait des colonnes ALIAS en double structurellement identiques tout en référençant une fonction de table avec un argument de collection nommée key = value (par exemple oss(s3_conn, filename = '...')). #108435 (groeneai).
  • Correction d’une erreur logique (une exception dans les builds de production, un arrêt brutal du serveur dans les builds de débogage et avec sanitizers) pouvant survenir lors d’un appel de fonction comportant plus d’un argument LowCardinality sur une table distribuée, par exemple concatAssumeInjective((SELECT toLowCardinality('p')), s) utilisé comme clé de GROUP BY avec remote(). Les messages d’erreur étaient Default functions implementation for LowCardinality is supported only with a single LowCardinality argument ou Expected the argument ... to have N rows, but it has M. #108871 (groeneai).
  • Correction de use_client_time_zone, qui était ignoré pour les littéraux string DateTime/DateTime64 interprétés sur le serveur (INSERT asynchrone, littéraux de SELECT). Le client transmet désormais son fuseau horaire local en tant que session_timezone lorsque use_client_time_zone est activé, afin que l’analyse côté serveur soit identique à celle du chemin INSERT synchrone. #109051 (groeneai).
  • Correction de max_execution_time (avec timeout_overflow_mode = 'throw') qui, dans certains cas, n’annulait jamais une requête : lorsque le surveillant interne des délais attendait déjà une requête dont l’échéance était plus tardive, une requête enregistrée ensuite avec une échéance plus proche pouvait passer totalement inaperçue et s’exécuter bien au-delà de sa limite de temps. #109792 (tiandiwonder).
  • Correction de l’erreur NOT_FOUND_COLUMN_IN_BLOCK lorsqu’une requête sur une table Distributed utilise _shard_num (ou shardNum()) devenu Nullable (par exemple avec FULL JOIN et join_use_nulls = 1) conjointement avec ORDER BY, l’analyseur étant activé. #109798 (groeneai).
  • Correction de l’erreur NOT_IMPLEMENTED (“Method getDataAt is not supported for Nullable(String)”) qui pouvait être levée par une jointure avec enable_join_runtime_filters = 1 (activé par défaut depuis la version 26.2) lorsque la clé de jointure côté construction était de type LowCardinality(Nullable(...)) et contenait des NULL, et que le filtre d’exécution se rabattait sur son filtre de Bloom. #109824 (groeneai).
  • Correction d’un plantage du serveur lorsqu’une expression lambda était passée là où une fonction d’ordre supérieur attend une valeur concrète (par exemple l’accumulateur de arrayFold, comme dans arrayFold(lambda, arr, another_lambda)). Lorsque enable_analyzer = 0, ces requêtes sont désormais rejetées avec ILLEGAL_TYPE_OF_ARGUMENT au lieu de provoquer un plantage. #109840 (groeneai).
  • Rejet de l’alias unnest de arrayJoin dans les filtres de politique de ligne, ce qui comble une faille par laquelle une telle politique pouvait déclencher une erreur logique column->size() == num_rows lors de la lecture. #109973 (Algunenano).
  • Correction des index de saut définis sur des sous-colonnes Tuple, qui n’étaient pas utilisés lorsque l’on accédait au champ via tupleElement(t, 'name'), tupleElement(t, N) ou t.N alors que le tuple complet était également lu dans la même requête (par ex. SELECT *). Toutes ces formes élaguent désormais les granules de la même manière que l’accès par sous-colonne nommée t.name. #110056 (groeneai).
  • Correction de l’erreur BAD_GET lors de la comparaison d’une colonne Array(String) avec un littéral de tableau (arr = ['x']) lorsque la colonne dispose d’un index de saut text, tokenbf_v1 ou ngrambf_v1. L’index traite désormais une telle comparaison comme un parcours complet au lieu de faire échouer toute la requête. #110057 (groeneai).
  • Correction d’une erreur ILLEGAL_COLUMN intempestive renvoyée par ALTER TABLE ... ADD COLUMN IF NOT EXISTS lorsque la colonne existe déjà au moment de l’application. Ce problème survenait lorsque la même colonne était ajoutée deux fois avec IF NOT EXISTS dans une même instruction, ou lorsqu’un ALTER concurrent ajoutait la colonne entre les phases de préparation et d’application. #110080 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (Bad cast from type ColumnLowCardinality to ColumnString) lorsque identity() (ou le résultat d’une sous-requête scalaire) encapsule une valeur contenant un LowCardinality imbriqué et que la requête utilise WITH TOTALS/WITH ROLLUP. #110138 (groeneai).
  • Correction d’une erreur NOT_FOUND_COLUMN_IN_BLOCK lors de l’utilisation de GROUP BY ALL sur une expression de tuple combinée à ORDER BY. #110206 (alexey-milovidov).
  • Correction d’une erreur NOT_FOUND_COLUMN_IN_BLOCK lors d’un INSERT dans une table comportant une contrainte CHECK qui fait référence à une sous-colonne (comme x.null d’une colonne Nullable ou arr.size0 d’un Array). #110208 (alexey-milovidov).
  • Correction de résultats de requête erronés produits par l’optimisation optimize_and_compare_chain (activée par défaut) : une condition déduite par transitivité de a < b AND b < c pouvait appliquer une sémantique de comparaison différente de celle des conditions d’origine lorsque la chaîne mélangeait des types soumis à des règles de conversion propres à chaque paire (par ex. Enum et String, Decimal et Float64, FixedString de largeurs différentes). Les conditions transitives ne sont désormais déduites qu’au sein de groupes de types partageant un même ordre de comparaison. #110621 (yariks5s).
  • Correction de optimize_injective_functions_in_group_by, qui modifiait le résultat de la requête pour GROUP BY GROUPING SETS et GROUP BY ... WITH TOTALS. Ce paramètre est censé préserver les résultats, mais l’élimination d’une fonction injective appliquée à une clé de regroupement produisait une valeur erronée (ou supprimait une ligne) pour les lignes où cette clé est absente de l’ensemble agrégé (un set non membre de GROUPING SETS) ou pour la ligne WITH TOTALS. La valeur erronée pouvait coïncider avec une valeur de clé réelle, rendant une ligne de super-agrégat impossible à distinguer d’un groupe réel dans le même jeu de résultats. #110721 (groeneai).
  • Correction d’un blocage du serveur lors de la multiplication d’un état de fonction d’agrégation median/quantile (reposant sur ReservoirSampler) par une très grande constante entière (par exemple medianState(x) * 18446744073709551615). La requête ne répondait plus aux demandes d’annulation et pouvait s’exécuter indéfiniment. #110779 (groeneai).
  • Correction d’un plantage (déréférencement d’un pointeur nul lors de l’analyse de requêtes) lorsque getSubcolumn est appelé sur un argument JSON avec un nom de sous-colonne interne d’indication de type, c’est-à-dire commençant par un marqueur de type sous forme d’accent grave. #110940 (groeneai).
  • Correction de map[key], qui renvoyait la valeur d’une occurrence ultérieure d’une clé dupliquée au lieu de la première. Lorsqu’une colonne Map contenait des clés dupliquées, le résultat de map[key] pouvait dépendre des lignes précédentes du même bloc et du paramètre optimize_functions_to_subcolumns, de sorte qu’un SELECT et un WHERE portant sur la même ligne pouvaient donner des résultats divergents. #111246 (groeneai).
  • Correction d’une erreur logique !rhs_literal->getValue().isNull() dans l’optimiseur d’expressions logiques lorsqu’une chaîne AND compare une colonne à une constante de valeur NULL d’un type non Nullable (par exemple un Variant de valeur NULL) avec use_variant_default_implementation_for_comparisons = 0. #111292 (groeneai).
  • Correction de résultats erronés lorsqu’une requête comporte un index de saut set et un prédicat de la forme (x AND <null-value>) OR y, où la valeur NULL est produite par une fonction (par exemple toInt64OrNull('x'), nullIf(1, 1), CAST(NULL AS Nullable(Int64))). Une telle valeur était encapsulée par __bitWrapperFunc et propageait NULL au lieu du masque « inconnu », si bien que l’index set pouvait élaguer des granules pourtant correspondants et que la requête renvoyait moins de lignes que prévu. #111604 (groeneai).
  • Correction de CREATE TABLE/ALTER TABLE avec l’analyseur, qui acceptait une expression DEFAULT/MATERIALIZED de colonne faisant référence à une colonne virtuelle telle que _table ou _database. L’insertion dans une telle table échouait avec NOT_FOUND_COLUMN_IN_BLOCK, et une colonne MATERIALIZED (qui ne peut pas être fournie explicitement) rendait toute insertion dans la table définitivement impossible. Ces expressions sont désormais rejetées dès la définition, comme c’est le cas sans l’analyseur. Les colonnes ALIAS (évaluées à la lecture) et EPHEMERAL (entrées d’insertion non stockées) portant sur des colonnes virtuelles continuent de fonctionner. #111776 (groeneai).
  • Correction de l’erreur UNKNOWN_QUERY_PARAMETER lorsqu’une vue paramétrée est appelée avec un argument issu d’une sous-requête de type Array, Tuple ou LowCardinality, par exemple SELECT * FROM v(param = (SELECT groupArray(x) FROM t)). #111790 (groeneai).
  • Correction de trois bogues dans les réécritures de l’analyseur qui transforment une chaîne de comparaisons en IN/NOT IN (x = c1 OR x = c2 OR ..., x != c1 AND x != c2 AND ... et has(const_array, x)). Pour une expression Variant avec use_variant_default_implementation_for_comparisons = 0, la réécriture faisait passer le type de résultat de l’expression de UInt8 à Nullable(UInt8), ce qui déclenchait une erreur LOGICAL_ERROR lors de la validation de l’arbre de requête dans les builds de débogage et les builds avec sanitizers. Pour une expression à structure dynamique (Dynamic, Array(Dynamic), JSON), la réécriture faisait échouer une requête pourtant fonctionnelle avec Illegal type ... of argument of function in, car IN rejette ce type d’arguments. Dans les deux sens, des résultats erronés étaient également renvoyés lorsqu’une constante ne pouvait pas être convertie sans perte vers le type de l’expression, par exemple une constante DateTime comportant une heure comparée à une colonne Date. #111924 (groeneai).
  • Correction de quantileDeterministic, quantilesDeterministic et medianDeterministic, qui renvoyaient des résultats différents pour les mêmes données à chaque exécution lorsque la requête utilisait les répliques parallèles, une table distribuée ou l’agrégation externe, ou lorsque les états étaient stockés dans une table (y compris à l’intérieur d’une valeur Dynamic). Les états écrits avant cette version sous l’ancienne notation de type non versionnée conservent le comportement précédent, leur format ne pouvant pas changer. #112052 (alexey-milovidov).
  • Correction de résultats erronés dus à une requête de recherche vectorielle qui corrompait le cache des conditions de requête. Une requête SELECT ... WHERE <condition> ORDER BY <distance function> LIMIT n sur une table dotée d’un index vector_similarity pouvait marquer des granules comme ne correspondant pas à <condition>, si bien qu’une requête ordinaire SELECT ... WHERE <condition> exécutée ensuite sur la même table renvoyait silencieusement moins de lignes. Les lectures liées à la recherche vectorielle n’écrivent plus dans le cache des conditions de requête. #112086 (groeneai).
  • Correction de l’omission de serialize_string_in_memory_with_zero_byte dans le plan de requête sérialisé. Avec serialize_query_plan = 1, un cluster où ce paramètre était désactivé pouvait présenter une divergence de représentation sous forme de chaîne en mémoire entre l’initiateur et le nœud exécutant le plan désérialisé : le paramètre était lu lors de la désérialisation mais jamais écrit lors de la sérialisation, et prenait donc toujours sa valeur par défaut sur le nœud d’exécution. #112131 (groeneai).
  • Correction d’un résultat erroné pour une JOIN dont l’expression ON contient une conjonction constante, par exemple ON l.id = r.id AND CAST(NULL AS Nullable(UInt8)). La constante était silencieusement supprimée par l’optimisation du plan de requête, si bien que la jointure s’exécutait comme si elle n’existait pas : les jointures INNER, LEFT SEMI et RIGHT SEMI renvoyaient des lignes correspondantes alors qu’une condition jamais vraie ne doit en renvoyer aucune, et RIGHT ANTI ne renvoyait aucune ligne au lieu de les renvoyer toutes. #112155 (vdimir).
  • Correction de résultats erronés lorsqu’une vue paramétrée est appelée avec un argument qui n’est pas une affectation parameter = value. Avec enable_analyzer = 0, ainsi que dans EXPLAIN SYNTAX avec les paramètres par défaut, des appels tels que pv(concat(name, '!')), pv(name, 'a'), pv(name != 'a') et pv(tuple(name = 'a')) liaient silencieusement un paramètre de la vue à une expression sans rapport, au lieu de signaler UNKNOWN_QUERY_PARAMETER comme le fait l’analyseur. Ces appels sont désormais rejetés de la même manière sur les deux chemins d’exécution. #112194 (groeneai).
  • Corrige un bogue qui faisait qu’une instruction CREATE VIEW ou CREATE FUNCTION dont la définition appelle substr, mid ou byteSlice sous une forme que la grammaire de substring ne peut pas réanalyser était acceptée, alors que les métadonnées écrites par ClickHouse pour cette instruction ne constituaient pas du SQL valide. La relecture ultérieure de cette définition échouait avec SYNTAX_ERROR et, au démarrage du serveur, interrompait le chargement des métadonnées, empêchant purement et simplement le serveur de démarrer. Ces définitions peuvent désormais être réécrites puis relues sans perte, et les fichiers de métadonnées existants de la forme substring(x, a, b, c) redeviennent lisibles : un serveur affecté démarre donc sans intervention manuelle. #112195 (groeneai).
  • Correction de résultats erronés, d’une sortie ORDER BY mal triée et d’une exception BAD_TYPE_OF_FIELD lorsque la clé primaire est de type Array ou Tuple et que la requête lui applique plus, minus, multiply, divide ou intDiv. Les opérations arithmétiques sur une valeur composée s’effectuent élément par élément, alors que la comparaison de valeurs composées est lexicographique ; l’analyse de la clé primaire ne considère donc plus une telle expression comme monotone. #112339 (groeneai).
  • Correction de l’erreur Code: 62 ... You must not specify ANY or ALL for PASTE JOIN. (SYNTAX_ERROR) levée par un serveur distant lorsqu’une requête contenant un PASTE JOIN est transmise à un autre serveur, par exemple lorsque la table de gauche est une table Distributed ou une fonction de table *Cluster. Le formateur produisait ALL PASTE JOIN, une écriture rejetée par l’analyseur, si bien que le serveur destinataire ne pouvait pas analyser la requête reçue. #112404 (groeneai).
  • Corrige un problème qui rendait une liste d’alias de colonnes telle que (SELECT 1) AS t(x) ou WITH t(x) AS (SELECT 1) invisible à l’intérieur de la fonction de table view : une référence à t.x y échouait avec UNKNOWN_IDENTIFIER, alors que la même construction fonctionne en dehors de view. #112690 (groeneai).
  • Corrige une erreur logique lors du formatage d’une requête lorsqu’une clause ARRAY JOIN ne contient aucune expression. Une telle clause est désormais rejetée dès l’analyse avec SYNTAX_ERROR, comme c’est le cas pour un USING vide, au lieu d’être acceptée puis formatée en un texte impossible à réanalyser. #112699 (groeneai).
  • Correction de connectionId, qui levait Context has expired lorsque les actions d’expression préparées pour une sous-requête s’exécutaient après la libération de leur contexte de construction. #112870 (clickgapai).
  • Corrige une erreur logique et des résultats erronés lorsqu’une condition JOIN ON de non-équijointure évaluée pendant la jointure contient arrayJoin. Une jointure hash, parallel_hash ou grace_hash rejette désormais une telle condition avec INVALID_JOIN_ON_EXPRESSION. Si l’expansion ne dépend que d’un seul côté, déplacez-la dans un ARRAY JOIN au sein d’une sous-requête, avant la jointure ; une condition dont l’argument de arrayJoin lit des colonnes des deux côtés doit être restructurée. #112889 (groeneai).
  • Correction de geohashesInBox, qui pouvait s’exécuter indéfiniment sur une zone d’aire nulle, comme geohashesInBox(0., 0., 180., 0., 12). Une telle requête renvoyait bien l’unique geohash attendu, mais effectuait environ 5e8 itérations de boucle à vide par ligne et ignorait aussi bien max_execution_time que KILL QUERY. #112976 (groeneai).
  • Une modification SETTINGS marquée comme écrite sans valeur est désormais rejetée lorsqu’elle porte une valeur autre que true, et la valeur d’une telle modification n’est plus jamais omise lors du formatage d’une requête. Auparavant, une charge utile JSON d’AST spécialement conçue pouvait exécuter un paramètre Bool avec false alors que system.query_log et formatQueryFromJSON affichaient la forme sans valeur. #113025 (alexey-milovidov).
  • Correction de l’erreur UNKNOWN_IDENTIFIER lors d’une sélection depuis une VIEW dont les types de colonnes déclarés diffèrent des types produits par sa requête interne, par exemple SELECT sum(length(arr)) FROM v où v déclare arr Array(UInt8) sur une colonne String. Avec la valeur par défaut optimize_functions_to_subcolumns = 1, l’optimiseur réécrivait length(arr) en une lecture de la sous-colonne arr.size0 et transmettait ce nom à la requête interne, qui ne possède pas cette sous-colonne. #113057 (groeneai).
  • Correction de l’annulation des lectures via la fonction de table postgresql et le moteur PostgreSQL. Une annulation survenant pendant le démarrage de la lecture pouvait provoquer une condition de concurrence sur le pointeur de transaction, et l’annulation pouvait être entièrement perdue, la requête continuant alors à s’exécuter jusqu’à ce que PostgreSQL la termine. Une annulation qui parvient à PostgreSQL avant que le statement COPY n’ait commencé à s’exécuter peut encore être ignorée. #113150 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (Next task callback is not set for query) lorsqu’une vue SQL SECURITY DEFINER ou SQL SECURITY NONE reposant sur une fonction de table de cluster (s3Cluster, urlCluster, fileCluster) était lue en tant que requête secondaire, par exemple via remote ou clusterAllReplicas. Cette imbrication non prise en charge est désormais rejetée avec une erreur de requête classique au lieu de faire planter le serveur dans les constructions debug et sanitizer. #113371 (groeneai).
  • Correction d’une erreur logique IDENTIFIER is not a table expression lorsqu’une fonction de table de la famille remote avec un argument view(...) est utilisée du côté gauche d’un JOIN ... USING dont la clé est un alias de la liste SELECT, avec analyzer_compatibility_join_using_top_level_identifier = 1. #113400 (groeneai).
  • Correction de l’erreur Not-ready Set is passed as the second argument for function 'in' lorsqu’une requête filtre system.databases sur la colonne name avec une sous-requête dans indexHint, par exemple SELECT count() FROM system.databases WHERE indexHint(name IN (SELECT 'foo')). Une telle requête était rejetée avec une erreur LOGICAL_ERROR sur les constructions release et faisait planter le serveur sur les constructions debug et sanitizer. #113556 (groeneai).
  • Correction d’un problème où une requête sur une colonne Variant ignorait max_execution_time et KILL QUERY. L’appel d’une fonction avec plusieurs arguments Variant résolvait la fonction une fois pour chaque alternative de chaque argument, et aucune des deux boucles chargées de ce travail ne vérifiait l’annulation : une telle requête ne pouvait donc pas être interrompue avant la fin de son exécution. #113612 (groeneai).
  • Correction de l’échec de ALTER TABLE ... MODIFY COLUMN avec Cannot specify codec for column type ALIAS lorsqu’un même statement transforme une colonne ALIAS en colonne physique (DEFAULT ou MATERIALIZED) et spécifie un CODEC. #113853 (groeneai).
  • Correction d’un plantage du serveur lors d’une jointure avec une expression ON constante sur un côté droit contenant un ARRAY JOIN. La lecture d’une colonne du côté droit répliquée de manière paresseuse utilisait un ColumnReplicated déjà libéré. #113901 (groeneai).
  • Correction d’une fuite de mémoire avec optimize_aggregation_in_order = 1 lorsque la clé de tri de la table est un préfixe strict de la clé GROUP BY et qu’une fonction d’agrégation dont l’état possède de la mémoire allouée sur le tas est utilisée, comme quantileDD. La mémoire du serveur augmentait à chaque requête de ce type jusqu’au redémarrage. #114010 (groeneai).
  • Correction de isProbablePrime sur UInt128/UInt256, qui ignorait max_execution_time et KILL QUERY. Un bloc de valeurs larges s’exécutait jusqu’au bout au sein d’un unique appel de fonction, sans point d’annulation, si bien qu’une requête pouvait se prolonger plusieurs minutes au-delà de son délai. #114032 (groeneai).
  • Correction de l’erreur Code: 46. DB::Exception: Unknown function exists. (UNKNOWN_FUNCTION) levée lorsqu’une clause PREWHERE contient un prédicat IN (subquery) et que rewrite_in_to_join = 1 (ou make_distributed_plan = 1, qui l’active d’office) est défini. La même requête écrite avec WHERE fonctionnait correctement. #114067 (RohithPariki).
  • Correction d’une erreur LOGICAL_ERROR dans arrayAutocorrelation lorsque l’argument est un tableau non vide dont le type d’élément est Nothing, par exemple SELECT arrayAutocorrelation([arrayMax([])]). Un tel argument est désormais rejeté avec ILLEGAL_COLUMN. Le littéral de tableau vide [] continue de renvoyer []. #114249 (groeneai).
  • CREATE TABLE et ALTER TABLE rejettent désormais les codecs avec perte, tels que SZ3, sur une colonne utilisée dans la clé de tri. #114531 (groeneai).
  • Correction d’une boucle infinie et impossible à annuler dans les fonctions hop et windowID lorsque la durée d’un argument d’intervalle, exprimée en secondes, est un multiple de 2^32 (par exemple, toIntervalDay(2147483648)) : la soustraction avec dépassement échappait à la vérification de débordement temporel et, avec des arguments constants, la boucle s’exécutait dès l’analyse, à un stade où la requête ne pouvait même pas être interrompue. Le même intervalle pouvait également faire tourner indéfiniment un thread d’arrière-plan d’une WINDOW VIEW ; une telle window view est désormais rejetée dès sa création. #114607 (alexey-milovidov).
  • Correction de l’erreur logique Not-ready Set is passed as the second argument for function 'in' pour une requête comportant une sous-requête IN lisant depuis un cluster distant : la requête échoue désormais avec l’erreur réelle de la sous-requête, par exemple un échec de connexion. #114983 (alexey-milovidov).
  • Correction d’un INSERT dans une table Log, TinyLog ou StripeLog échouant lors de la validation des tailles de fichiers enregistrées. Une telle insertion pouvait laisser ses lignes dans la table bien qu’elle ait signalé une erreur, ou rendre les fichiers de données d’une colonne de tableau incohérents entre eux. La lecture d’une table Log ou TinyLog dont la colonne de tableau ne contient aucun élément alors que ses offsets en annoncent lève désormais une erreur indiquant le nom de la colonne, au lieu de lire au-delà de la fin des éléments. Une colonne d’éléments partiellement écrite était déjà rejetée. #115027 (groeneai).
  • Correction de l’erreur TYPE_MISMATCH (“CAST AS Array can only be performed between same-dimensional array types”) pour une requête comportant une constante de type Array(Map(...)), comme has([map('k', 'v')], m), exécutée sur une table Distributed ou avec des répliques parallèles. #115883 (alexey-milovidov).
  • Correction de résultats erronés lors d’une jointure avec une vue ou view() dont la colonne projetée porte le même nom qu’une colonne de l’une de ses propres relations internes, par exemple SELECT expr(x.c0) AS c0 FROM a AS x, b AS y. L’optimiseur d’ordre des jointures intégrait une telle expression au graphe de jointure aplati, puis l’appliquait deux fois : la requête renvoyait alors silencieusement des valeurs faussées. Lorsque l’expression modifiait également le type de la colonne, la même cause produisait à la place une erreur AMBIGUOUS_COLUMN_NAME lors de la planification. #116152 (groeneai).
  • Correction d’une exception dans les sous-requêtes corrélées lorsque des colonnes externes deviennent Nullable avec group_by_use_nulls et ROLLUP/CUBE. #100365 (alexey-milovidov).
  • Rejet de max_replication_lag_to_enqueue = 0 sur les bases de données Replicated. Avec cette valeur, la vérification de non-synchronisation après récupération était trivialement vraie, ce qui provoquait l’arrêt du serveur avec LOGICAL_ERROR dans les builds de débogage et avec sanitizer. 0 est désormais rejeté dès l’analyse avec BAD_ARGUMENTS, quelle que soit la source (CREATE DATABASE ... SETTINGS, bloc <database_replicated> de la configuration du serveur, rejeu d’ATTACH et rejeu des métadonnées existantes lors d’une mise à niveau). La plus petite valeur valide est 1. #106006 (groeneai).
  • Interdiction de créer un index de saut minmax sur les colonnes JSON (Object) afin d’éviter l’exception NO_COMMON_TYPE lors de l’insertion de tableaux de types mixtes. Utilisez plutôt des sous-colonnes typées (par exemple, INDEX idx json.field TYPE minmax). #106094 (linhaojie).
  • Correction de JSON_QUERY/JSON_VALUE/JSON_EXISTS, qui renvoyaient Dynamic au lieu de String/UInt8 avec des arguments Dynamic. #106877 (Avogar).
  • Correction d’un dépassement d’entier signé lorsqu’un paramètre de délai d’attente, tel que interactive_delay, était défini sur une valeur très élevée : l’attente pouvait expirer immédiatement, et le serveur pouvait s’arrêter brutalement sous l’Undefined Behavior Sanitizer. #106961 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (« Unsupported argument types ») dans la fonction geohashesInBox lorsque ses arguments de coordonnées mélangeaient des valeurs constantes et non constantes, ou lorsqu’une coordonnée était de type BFloat16. Les arguments Float32 mêlant constantes et non-constantes fonctionnent désormais, et les arguments BFloat16 sont rejetés avec une erreur explicite. #107063 (groeneai).
  • Correction de l’en-tête de CSVWithNames et CSVWithNamesAndTypes, qui comportait moins de colonnes que les données lorsque output_format_csv_serialize_tuple_into_separate_columns est activé (valeur par défaut). L’en-tête (ainsi que la ligne des types) aplatit désormais les colonnes Tuple en leurs champs terminaux avec des noms pointés (par exemple t.a, t.b), afin que le nombre de colonnes de l’en-tête corresponde à celui des données. Un nouveau paramètre, output_format_csv_header_serialize_tuple_into_separate_columns (1 par défaut), contrôle ce comportement ; il peut être défini sur 0 pour rétablir l’ancien en-tête à nom unique. #107371 (groeneai).
  • Correction d’une fuite de mémoire dans le mongo-c-driver intégré, susceptible de se produire lors de la lecture depuis MongoDB (par exemple via un dictionnaire MongoDB ou la fonction de table mongodb) lorsqu’une erreur de lecture pouvant donner lieu à une nouvelle tentative était suivie d’un échec de la sélection du serveur pour cette nouvelle tentative. #107448 (groeneai).
  • Correction d’une erreur logique (WhichDataType(const_type).isArray()) et d’un arrêt brutal du serveur lors de l’analyse de la clé primaire lorsque pointInPolygon est appelée avec un argument polygone constant d’un type enveloppe tel que Variant ou Dynamic (par exemple pointInPolygon((x, y), if(c, [(0, 0), ...], NULL))). #107589 (groeneai).
  • Correction d’erreurs logiques (Bad cast) causées par une suppression incohérente de LowCardinality imbriqué dans des colonnes Variant et Dynamic, par exemple dans concat, format et l’analyse de la clé primaire. #107773 (Avogar).
  • Correction de l’analyse des paramètres de commande des fonctions définies par l’utilisateur de type executable, afin que les caractères de substitution dont le nom est vide ou invalide ne soient plus acceptés comme paramètres. #107983 (goutamadwant).
  • Correction de accurateCastOrNull appliquée à un Tuple dont un élément est Dynamic/Variant : une valeur NULL réellement présente dans la source était traitée comme un échec de conversion. Pour un élément cible Nullable, une valeur NULL source reste désormais un élément NULL (comme pour une source Tuple(Nullable(...)) simple) ; pour un élément cible non Nullable, une valeur NULL source rend désormais l’ensemble du tuple NULL (comme la référence non Dynamic) au lieu de produire la valeur par défaut de l’élément. Les échecs d’analyse ou de dépassement rendent toujours l’ensemble du tuple NULL. #108023 (groeneai).
  • La fonction de table mongodb accepte désormais oid_columns passé en argument nommé (par ex. oid_columns='_id'), au lieu de le rejeter avec BAD_ARGUMENTS. #108039 (alexey-milovidov).
  • Il est désormais possible d’interroger un dictionnaire range_hashed ou complex_key_range_hashed utilisant une plage DateTime64, Decimal ou à virgule flottante avec un argument correspondant passé à dictGet/dictHas. Auparavant, ces requêtes échouaient avec must be convertible to Int64, et les intervalles ouverts des plages Decimal/DateTime64 renvoyaient à tort la valeur par défaut. #108052 (alexey-milovidov).
  • Correction d’un arrêt brutal du serveur (Logical error dans IColumn::insertFrom) lors de la conversion d’un Array(Dynamic) ou Array(Variant) en QBit avec accurateCastOrNull, par ex. accurateCastOrNull(CAST(range(114), 'Array(Dynamic)'), 'QBit(Float32, 114)'). #108288 (groeneai).
  • Correction d’un plantage du serveur (Received signal 4, instruction illégale) pouvant être déclenché par un flux du protocole TCP natif malformé ou désynchronisé. Le champ initial_address de ClientInfo est désormais vérifié comme étant un host:port numérique avant son analyse, au lieu de laisser un port non numérique atteindre la fonction libc getservbyname, qui est interceptée. #108410 (groeneai).
  • Correction d’une allocation mémoire excessive lors de la désérialisation d’états de fonctions d’agrégation forgés pour mannWhitneyUTest, rankCorr, largestTriangleThreeBuckets, quantileGK, sequenceMatch/sequenceCount et groupArrayIntersect. Un état malformé pouvait déclarer un nombre d’éléments démesuré et amener le serveur à tenter d’allouer des dizaines de gigaoctets à partir de quelques octets en entrée ; ces états sont désormais rejetés avec TOO_LARGE_ARRAY_SIZE. #108465 (groeneai).
  • Correction d’une erreur logique (Bad cast from type DB::ColumnNullable to DB::ColumnString) et de résultats potentiellement incorrects lors de l’utilisation de group_by_use_nulls avec une clé de regroupement constante LowCardinality dans GROUPING SETS/ROLLUP/CUBE. #108771 (alexey-milovidov).
  • Correction de l’erreur TYPE_MISMATCH dans mapFilter, mapSort, mapReverseSort, mapPartialSort et mapConcat lorsqu’une valeur Map contient une Map imbriquée avec LowCardinality, par exemple mapFilter((k, v) -> 1, map('a'::LowCardinality(String), map('x'::LowCardinality(String), 'y'))). #108798 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (« Inconsistent AST formatting ») qui provoquait l’arrêt du serveur dans les builds debug et sanitizer lors du formatage d’un type de données Tuple combinant des éléments nommés et non nommés (par exemple Tuple(a UInt8, UInt16)). #108915 (groeneai).
  • JSONExtract respecte désormais cast_string_to_date_time_mode lors de la conversion de valeurs JSON de type string en DateTime/DateTime64, de manière cohérente avec CAST. #109252 (Utkal059).
  • Correction de IN lorsque l’argument de droite est une simple colonne de type tableau : le résultat renvoyé était silencieusement incorrect (toujours faux) ou une exception était levée. Désormais, x IN arr se comporte comme has(arr, x). #109416 (alexey-milovidov).
  • Correction d’une erreur LOGICAL_ERROR dans arrayFold appliquée à un argument non constant de type Array(LowCardinality(T)) (par exemple arrayFold((acc, x) -> acc + x, materialize([1, 2, 3]::Array(LowCardinality(Int64))), toInt64(0))), qui échouait avec le message « Arguments of ‘plus’ have incorrect data types » (provoquant l’arrêt du serveur dans les builds debug et sanitizer). #109462 (groeneai).
  • Correction d’une exception ILLEGAL_COLUMN dans conv : les arguments FixedString sont désormais correctement convertis en String. #109771 (hp77-creator).
  • Correction de résultats incorrects lors de lectures via des fonctions de table avec FINAL / SAMPLE lorsque serialize_query_plan = 1, et correction de la clé du cache de statistiques des tables de hachage d’agrégation pour les fonctions de table (auparavant, toutes les fonctions de table partageaient la même entrée de préallocation). #109847 (azat).
  • Correction de l’analyse des valeurs numériques 1/0 en tant que valeurs Bool dans les types conteneurs (par exemple Array(Bool), Tuple(Bool, ...)). Auparavant, [1,0] échouait avec CANNOT_READ_ARRAY_FROM_TEXT, alors que [true,false] fonctionnait. #109976 (groeneai).
  • Correction de l’erreur NOT_IMPLEMENTED (Method getDataAt is not supported for Nullable(String) in case if value is NULL) survenant lorsqu’un index text est construit sur Array(LowCardinality(Nullable(String))) (y compris les champs Nested stockés de cette manière) et qu’un tableau indexé contient un élément NULL. Les éléments NULL des tableaux sont désormais ignorés lors de la construction de l’index, comme c’est le cas pour Array(Nullable(String)). #110055 (groeneai).
  • Correction de résultats incorrects de subBitmap, bitmapSubsetInRange et bitmapSubsetLimit pour les bitmaps encore stockés dans la représentation compacte, y compris pour les valeurs d’éléments UInt64 et Int64 supérieures à 2^32, qui étaient tronquées à 32 bits. Les comparaisons effectuées par les fonctions bitmap sur des types d’éléments signés utilisent désormais systématiquement la valeur non signée du type d’élément : dans un bitmap Int8, l’élément -1 est ainsi comparé en tant que 255 et non en tant que valeur étendue par le signe 4294967295. Cela corrige également bitmapMin, bitmapMax, bitmapContains et bitmapTransform sur les bitmaps ayant dépassé la représentation compacte. Les requêtes qui transmettaient des seuils étendus par le signe doivent être adaptées : sur un bitmap Int8, bitmapSubsetInRange(bm, 4294967168, 4294967296) devient bitmapSubsetInRange(bm, 128, 256). Correction également de groupNumericIndexedVector, qui renvoyait pour les colonnes d’index Int8 et Int16 des résultats différents de ceux obtenus avec des types d’index plus larges, ainsi que de numericIndexedVectorGetValue, qui renvoyait 0 pour les index négatifs. La documentation des fonctions bitmap a été corrigée, notamment pour les fonctions de sous-ensemble décrites à tort comme utilisant une indexation à partir de 1, et pour la prise en charge des types signés par bitmapBuild / bitmapToArray. #110072 (RamiDarwiche).
  • Correction de cas de dépassement de capacité signé au comportement indéfini dans l’arithmétique des intervalles, pour WITH FILL STEP de type intervalle et pour les fonctions d’intervalle date/heure add* / subtract* avec des écarts extrêmes : les valeurs d’intervalle extrêmes produisent désormais un résultat déterministe au lieu de dépendre d’un dépassement de capacité signé. Correction également d’un blocage de WITH FILL de type intervalle avec un très grand pas YEAR, qui faisait auparavant reculer le calendrier. #110158 (groeneai).
  • Correction d’une explosion du temps d’exécution en complexité quadratique (et de l’absence de prise en compte de max_execution_time) lors de l’agrégation dans l’ordre avec un regroupement sur plusieurs clés dont l’ordre de tri ne constitue qu’un préfixe de la clé de regroupement. #110159 (alexey-milovidov).
  • Correction des comparaisons qui levaient auparavant ILLEGAL_TYPE_OF_ARGUMENT lorsque les opérandes n’avaient pas de supertype commun minimal. Les tableaux tels que Array(Int64) et Array(UInt64), ou Array(Int256) et Array(UInt256), peuvent désormais être comparés lexicographiquement avec =, !=, <, <=, >, >=, IS DISTINCT FROM et IS NOT DISTINCT FROM. Les comparaisons compatibles avec NULL telles que isDistinctFrom / isNotDistinctFrom comparent également par valeur les paires numériques ou décimales (hors tableaux) prises en charge, au lieu de lever une exception. Les comparaisons d’égalité fonctionnent aussi pour les tableaux contenant des éléments Tuple(Nullable(...)) imbriqués. #110245 (diegomestre2).
  • Correction d’un dépassement de capacité d’entier signé (UBSan) lors de la conversion d’une valeur DateTime64 extrême (par exemple INT64_MIN) en Time/Time64 dans un fuseau horaire à décalage négatif. #110451 (groeneai).
  • La conversion des valeurs à virgule flottante hors plage (BFloat16, Float32, Float64) et des valeurs UInt64 supérieures à Int64::max() en DateTime et Time sature désormais de manière fiable aux bornes de la plage sur toutes les architectures (auparavant, le résultat d’une conversion hors plage dépendait de l’architecture et pouvait reboucler sur x86-64), et les valeurs non finies (NaN, inf) lèvent désormais une exception au lieu de produire un résultat arbitraire. Le même correctif s’applique au chemin de conversion des flottants vers Date de toDate, ainsi qu’aux conversions de UInt64 et des entiers larges ((U)Int128, (U)Int256) en Date et Date32, qui pouvaient renvoyer un jour antérieur à l’epoch au lieu de saturer. La conversion d’un nombre en Time sature désormais à la plage du type, quel que soit le type numérique source (auparavant, les valeurs issues d’entiers non signés étroits ou d’entiers larges étaient stockées sans écrêtage et n’étaient écrêtées qu’à l’affichage). Par ailleurs, le cast exact vers Time ne rejette plus les valeurs négatives prises en charge par Time et ne sature plus silencieusement les valeurs qui ne peuvent pas y être représentées. Le cast exact d’un nombre vers Date32 (accurateCast, accurateCastOrNull) ne sature plus silencieusement une valeur hors plage ou non finie : il lève une exception ou renvoie NULL, comme c’était déjà le cas pour Date et DateTime, et les variantes OrDefault renvoient la valeur par défaut de Date32 pour une telle entrée. Le cast exact d’un nombre à virgule flottante non entier vers Date, Date32, DateTime ou Time est désormais rejeté au lieu d’être silencieusement tronqué. Correction également de la documentation de la fonction toDateTime32, qui affichait par erreur des exemples de toDateTime64 et décrivait la plage de valeurs de DateTime64. #110459 (alexey-milovidov).
  • Correction de l’échec de l’inférence de schéma avec l’erreur Map cannot have a key of type LowCardinality(Nullable(String)) lors de la lecture de fichiers ORC dont les clés de map sont encodées par dictionnaire, y compris les fichiers ORC écrits par Spark et ceux écrits par ClickHouse lui-même lorsque output_format_orc_dictionary_key_size_threshold est activé. #110492 (tiandiwonder).
  • Correction d’une erreur logique (Function node with name '...' is not resolved as ordinary function) pouvant survenir lorsque optimize_or_like_chain était activé et qu’une fonction d’agrégation ou une fonction de fenêtre figurait à gauche d’un LIKE/ILIKE/match au sein d’une chaîne OR. #110641 (groeneai).
  • Correction d’un dépassement de capacité d’entier signé dans toStartOfInterval avec un nombre extrême d’intervalles MONTH, WEEK ou DAY sur des valeurs Date/Date32/DateTime64. #110688 (groeneai).
  • Correction de readWKT et readWKTPoint, qui renvoyaient pour POINT EMPTY des coordonnées non initialisées (en mode scalaire) ou obsolètes, issues de la ligne précédente (en mode vectorisé). POINT EMPTY est désormais rejeté avec CANNOT_PARSE_TEXT, car un Point ClickHouse est un Tuple(Float64, Float64) fixe, dépourvu de représentation vide. #110692 (groeneai).
  • Correction de flipCoordinates, qui perdait le type Geometry : pour un argument Geometry, le résultat est de nouveau typé Geometry au lieu du Variant(...) sous-jacent, ce qui permet de le passer directement à des fonctions comme areaCartesian. #110694 (groeneai).
  • Correction de la sérialisation WKB non canonique d’un polygone vide : wkb(POLYGON EMPTY) émet désormais numRings = 0 au lieu d’un anneau parasite unique à zéro point, de sorte qu’un aller-retour WKB d’un polygone vide standard restitue une valeur identique. #110796 (groeneai).
  • Les valeurs numériques 1/0 lors de l’analyse de Bool en JSON respectent désormais le paramètre allow_special_bool_values. Auparavant, une entrée JSON telle que {"v":1} pour Variant(Bool, UInt32) était lue comme Bool même avec allow_special_bool_values_inside_variant = 0, car les analyseurs JSON de Bool ignoraient ce paramètre et Bool bénéficie d’une priorité de désérialisation Variant plus élevée que les types entiers. #110835 (groeneai).
  • Correction de throwIf(notLike(col, pattern)) (et d’autres appels throwIf appliqués à une fonction) qui levait systématiquement une exception lorsque col est de type LowCardinality. L’implémentation par défaut de LowCardinality exécutait throwIf sur l’ensemble du dictionnaire, lequel contient toujours la valeur par défaut réservée, même lorsqu’aucune ligne n’y fait référence ; une valeur non nulle dans cet emplacement inutilisé amenait donc throwIf à lever une exception pour des données ne satisfaisant pas la condition. #110864 (groeneai).
  • Correction d’un plantage lorsqu’une fonction d’agrégation avec le combinateur -Tuple était utilisée avec RESPECT NULLS et produisait un état intermédiaire (-State, agrégation distribuée, WITH ROLLUP/WITH CUBE). La fonction combinée portait le nom d’une variante d’état erronée, si bien que son type AggregateFunction sérialisé était de nouveau résolu vers une disposition en mémoire incompatible lors d’un aller-retour. #110930 (groeneai).
  • Correction de l’erreur Nested type LineString cannot be inside Nullable type (ILLEGAL_TYPE_OF_ARGUMENT) lors de la lecture d’une colonne spatiale MySQL nullable (LINESTRING, POLYGON, MULTILINESTRING, MULTIPOLYGON, MULTIPOINT ou le type générique GEOMETRY) via la fonction de table mysql ou les moteurs de table/base de données MySQL. Ces colonnes sont désormais converties par défaut en Nullable(String), qui contient la valeur telle que MySQL la renvoie (un préfixe SRID de 4 octets suivi de la charge utile WKB). POINT reste associé à Nullable(Point). #110943 (groeneai).
  • Correction de la sérialisation Native des wrappers SimpleAggregateFunction contenant des états d’agrégation versionnés à destination des pairs plus anciens. #110997 (groeneai).
  • Correction de l’erreur CANNOT_CONVERT_TYPE pour les constantes de type Variant (y compris Geometry) dans les requêtes distribuées et les requêtes utilisant des répliques parallèles. #111136 (fm4v).
  • Correction d’une erreur LOGICAL_ERROR (“Lambda resolved type … is not equal to type from actions DAG …”) lorsque le corps de la lambda d’une fonction d’ordre supérieur est un if/multiIf à condition constante combinant une branche littérale Bool et une branche de comparaison UInt8, par exemple arrayFilter(p -> multiIf(false, true, p = 'ALL'), ['ALL']). #111245 (groeneai).
  • Correction d’un dépassement de capacité d’entier signé dans toStartOfInterval avec des intervalles inférieurs à la seconde : pour les valeurs DateTime64 situées à moins d’un intervalle de la borne inférieure de Int64, le résultat arrondi bouclait silencieusement vers une valeur positive aberrante ; la fonction lève désormais une exception DECIMAL_OVERFLOW. #111370 (alexey-milovidov).
  • Correction d’une exception (CANNOT_CONVERT_TYPE ou erreur logique) dans les requêtes distribuées utilisant le combinateur -Tuple avec un modificateur RESPECT NULLS / IGNORE NULLS sous des combinateurs imbriqués, par exemple anyRespectNullsStateTuple(...) IGNORE NULLS. #111570 (alexey-milovidov).
  • Corrige une tentative d’analyse textuelle de FixedString qui laissait des octets partiellement ajoutés dans la colonne en cas d’échec de l’analyse, ce qui pouvait provoquer une erreur logique Sizes of nested column and null map of Nullable column are not equal lors de la sérialisation de colonnes Variant/Array/Nullable, ou un décalage silencieux des octets du résultat. #111908 (groeneai).
  • Correction d’une exception et de données ignorées silencieusement lors de la lecture d’une sous-colonne dynamique (un chemin à l’intérieur d’une colonne JSON ou Dynamic) via une table Buffer. #111948 (alexey-milovidov).
  • Correction d’une complexité quadratique dans countSubstringsCaseInsensitiveUTF8, qui pouvait faire durer la fonction plusieurs minutes sur une chaîne de quelques mégaoctets comportant de nombreuses correspondances. Le décalage en caractères de chaque correspondance est désormais calculé de manière incrémentale au lieu d’être recompté depuis le début de la ligne. #112003 (groeneai).
  • Correction d’un dépassement d’entier signé dans la surcharge à trois arguments de toStartOfInterval : pour un argument origin proche de la borne inférieure de Int64, la fonction renvoyait une valeur ayant bouclé au lieu de signaler une erreur. #112045 (alexey-milovidov).
  • Les états d’agrégation sont désormais interdits dans les paramètres des fonctions de table, car ils n’y sont pas du tout pris en charge. #112087 (PedroTadim).
  • Correction de la fonction de table values, qui rendait une constante Bool non littérale (par ex. CAST(true, 'Nullable(Bool)')) insérée dans une colonne String/textuelle sous la forme 1/0 au lieu de true/false. #112308 (yakov-olkhovskiy).
  • Corrige une erreur logique (qui provoque un arrêt dans les builds de débogage et avec sanitizer) lorsque finalizeAggregation est appliquée à une colonne dont les états d’agrégation proviennent de fonctions différentes partageant une même représentation d’état mais produisant des types finaux différents — par exemple quantileState et quantilesState(0.9) réunis par un UNION ou produits via arrayReduce. Ces requêtes lèvent désormais une erreur ordinaire au lieu de planter. #112662 (zainulabidin302).
  • Correction de l’analyse des entiers ne tenant pas dans le type cible dans Poco::NumberParser, utilisé pour lire les nombres JSON. Un tel nombre était silencieusement converti en une valeur erronée (par exemple, 18446744073709551617 devenait 1) au lieu d’être rejeté, et un nombre UInt64 supérieur au maximum de Int64 ne survivait pas à un aller-retour JSON de l’AST. #112904 (alexey-milovidov).
  • Correction d’une constante Bool située à droite de IN, qui était convertie en '1'/'0' pour un membre gauche de type String au lieu des valeurs canoniques 'true'/'false' (comportement désormais cohérent avec CAST et la fonction de table values). #113051 (yakov-olkhovskiy).
  • Correction d’un formatage incohérent de l’AST pour viewIfPermitted : une fonction normalement écrite sous forme d’opérateur (par ex. not) dans la branche ELSE de la forme fonction de table, et la forme expression viewIfPermitted(...) formatée à tort avec ELSE. #113652 (alexey-milovidov).
  • Correction d’un comportement indéfini et de résultats erronés de avg sur Date/DateTime/DateTime64/Time/Time64 : la moyenne est désormais calculée de manière exacte dans l’espace des entiers, ce qui corrige à la fois le dépassement de capacité à la limite de Int64 (UB, résultat erroné sur x86) et la perte de précision de Float64 au-delà de 2^53 (visible à l’échelle de la nanoseconde). #113912 (alexey-milovidov).
  • Correction d’une fuite de mémoire dans le codec de compression expérimental SZ3 : lorsque la compression de données peu compressibles ou corrompues échouait avec une exception, des tampons de travail temporaires n’étaient pas libérés. #113916 (alexey-milovidov).
  • Correction de hasAny, hasAll, has, indexOf, mapContainsKey, mapContainsValue et mapContains, qui renvoyaient trop peu de lignes ou échouaient avec TOO_LARGE_STRING_SIZE lorsqu’un index bloom_filter était interrogé avec une constante FixedString. L’index hachait la forme complétée de la constante alors que la fonction compare la forme non complétée, si bien qu’un granule correspondant était ignoré. #114089 (alexey-milovidov).
  • Correction de min et max sur les colonnes DateTime64, qui renvoyaient des résultats erronés lorsque l’agrégat était compilé JIT et que les données contenaient des timestamps antérieurs à 1970. is_signed n’était pas spécialisé pour DateTime64 et Time64, qui dérivent de Decimal64 ; le code généré comparait donc les nombres de ticks comme des entiers non signés, de sorte qu’un timestamp antérieur à 1970 était à tort retenu par max et écarté par min. #114168 (groeneai).
  • Correction de arrayExists(x -> x = needle, arr), qui renvoyait un résultat erroné ou échouait avec TOO_LARGE_STRING_SIZE lorsque l’élément du tableau et le needle étaient de types string différents (par exemple un needle String comparé à un élément Array(FixedString(N))). L’optimisation optimize_rewrite_array_exists_to_has, activée par défaut, réécrivait un tel appel en has, qui, contrairement à =, ne tient pas compte de la complétion par des zéros lors de la comparaison. #114496 (groeneai).
  • Correction de l’évaluation par l’index de texte de l’opérateur LIKE/ILIKE appliqué à des conteneurs Map ou JSON. #114544 (ahmadov).
  • Correction d’un comportement indéfini et de résultats incorrects aux bornes dans quantile et quantiles pour les valeurs DateTime64 et Decimal larges. #114919 (alexey-milovidov).
  • Correction de if et multiIf, qui renvoyaient une grande valeur positive au lieu d’une valeur négative lorsqu’une branche Time était combinée avec Time64, DateTime ou DateTime64 et que l’expression était compilée JIT. #115146 (groeneai).
  • Correction de timezoneOffset (alias timeZoneOffset), qui renvoyait un décalage erroné d’exactement un jour pour les time zones dont le décalage UTC diffère d’un jour entier de leur décalage au 1970-01-01, comme Pacific/Kiritimati et Pacific/Apia après le franchissement de la ligne de changement de date, ainsi que pour la plupart des fuseaux avant 1970. Cela corrige également parseDateTime, parseDateTimeOrNull, parseDateTime64, parseDateTimeInJodaSyntax, EXTRACT(TIMEZONE_HOUR / TIMEZONE_MINUTE ...), le %z de formatDateTime, toUTCTimestamp et fromUTCTimestamp, qui s’appuient sur ce décalage. #115332 (groeneai).
  • Correction de l’erreur Code: 349. Cannot convert NULL value to non-Nullable type lorsqu’un prédicat has ou notHas contient un élément de tableau NULL et que la clé primaire est de type String, Array(String) ou Map(String, String), ainsi que des résultats incorrects lorsque cet élément est un Dynamic ou un Variant contenant NULL. La valeur NULL est désormais retirée du set lors de l’analyse de la clé, comme c’était déjà le cas pour les autres types de clé. #115647 (groeneai).
  • Correction de cast_keep_nullable, qui ne préservait pas le caractère nullable lorsque le type cible était LowCardinality. La conversion d’une valeur susceptible d’être NULL vers LowCardinality(T) levait l’erreur CANNOT_INSERT_NULL_IN_ORDINARY_COLUMN au lieu de produire LowCardinality(Nullable(T)). #115851 (groeneai).

Corrections relatives aux index et au cache de requêtes

  • Correction de la lecture des sous-colonnes d’une colonne qui possède une expression DEFAULT et qui n’est pas matérialisée dans une partie de données (par exemple après ALTER TABLE ADD COLUMN ou pendant ALTER TABLE MATERIALIZE COLUMN) : la sous-colonne était remplie avec les valeurs par défaut du type au lieu de l’expression DEFAULT évaluée, et les fonctions de distance vectorielle transposées (cosineDistanceTransposed et autres) appliquées à ces colonnes QBit échouaient avec l’erreur SIZES_OF_ARRAYS_DONT_MATCH. #110636 (alexey-milovidov).
  • Correction de l’exception Not-ready Set is passed as the second argument qui pouvait survenir lorsque la construction d’un set de sous-requête IN pendant l’analyse de la clé primaire échouait silencieusement (par exemple, en cas de dépassement du délai d’une sous-requête avec overflow_mode = 'break'), le set n’étant alors jamais construit pour le pipeline de la requête. #107924 (alexey-milovidov).
  • Correction d’un SELECT qui échouait avec NOT_FOUND_COLUMN_IN_BLOCK sur une table dotée d’un index de saut de données set lorsqu’une politique de lignes la filtre à l’aide d’une condition toujours vraie combinée à une vérification sur une colonne non indexée. #107971 (tiandiwonder).
  • getClientHTTPHeader est désormais correctement traitée comme non déterministe : son résultat n’est donc plus réutilisé à tort par le cache des résultats de requêtes. #108029 (alexey-milovidov).
  • Correction de résultats erronés lorsque le cache des conditions de requête (use_query_condition_cache = 1) réutilisait une exclusion de marques issue d’un index de saut pour une requête ayant exécuté un ensemble différent d’index de saut, par exemple avec use_skip_indexes = 0, ignore_data_skipping_indices ou un mode use_skip_indexes_for_disjunctions différent. Les entrées du cache issues des index de saut ont désormais pour clé l’ensemble effectif des index de saut exécutés, tandis que les entrées du cache des conditions de requête au niveau des lignes restent réutilisables dans tous ces cas. #108548 (groeneai).
  • Correction d’un blocage : une requête lisant via le cache du système de fichiers pouvait continuer d’attendre la fin d’un téléchargement en cours après avoir été annulée avec KILL QUERY, et la suppression d’une vue matérialisée actualisable, ou l’exécution de SYSTEM STOP VIEW sur celle-ci, restait bloquée lorsque son rafraîchissement était coincé dans une telle attente. #109116 (murphy-4o).
  • Correction de résultats de requête erronés dus à l’élagage incorrect de granules par l’index de clé primaire pour les tables ayant une colonne de clé inversée (DESC) lorsque les parties de données n’ont pas de marque finale (granularité non adaptative, index_granularity_bytes = 0). #109901 (nihalzp).
  • Correction de l’erreur logique Expected CommonSubplanReferenceStep to reference CommonSubplanStep, de l’erreur Subplan cannot be used to build pipeline et de l’erreur logique Trying to extract chunk from ChunkBuffer before all inputs are finished pour IN (subquery) lorsque la sous-requête contient une sous-requête corrélée et que le set est construit pendant l’analyse des index. #110491 (alexey-milovidov).
  • Correction de l’erreur NOT_FOUND_COLUMN_IN_BLOCK pour les fonctions de distance transposées sur des colonnes QBit (comme cosineDistanceTransposed) appelées avec un vecteur de référence constant ou issu d’une sous-requête scalaire lorsque les répliques parallèles sont activées. #110729 (alexey-milovidov).
  • Correction d’un index de texte qui n’était pas utilisé lorsque son expression contenait une comparaison avec une chaîne vide telle que arrayFilter(s -> s != '', ...), car le paramètre optimize_empty_string_comparisons réécrit la comparaison en notEmpty uniquement côté requête. #112115 (Ergus).
  • Les colonnes ne sont plus supprimées lorsqu’elles sont référencées à la fois dans les conditions de filtrage PREWHERE et WHERE, même si elles sont réécrites en colonne virtuelle d’index de texte. #114460 (ahmadov).
  • Correction de l’erreur logique Multi-block postings must be compressed dans les requêtes portant sur des tables dotées d’un index de texte en mode d’application différée des posting-lists, lorsque la requête était annulée pendant la lecture. #114636 (CurtizJ).
  • Correction de résultats erronés lorsqu’une clause WHERE contenait une expression NULL sous un NOT, par exemple SELECT count() FROM t WHERE NOT (id >= 20 AND NULL). L’analyse d’index considérait à tort une telle condition comme toujours vraie sur l’ensemble de la plage, si bien que le filtre était ignoré : count() renvoyait des lignes rejetées par la requête, et ALTER TABLE ... DELETE avec cette condition supprimait des lignes qui auraient dû être conservées. #115152 (groeneai).
  • Correction d’une LOGICAL_ERROR (Join is supported only for pipelines with one output port) pour une jointure full_sorting_merge avec query_plan_join_shard_by_pk_ranges activé, lorsque l’un des deux côtés ne contient plus aucune partie de données après élagage, que ce soit par la clé primaire, par les statistiques de colonne ou parce que la table est vide. #115352 (alexey-milovidov).
  • Désactivation du cache des conditions de requête pour les filtres portant sur _part_starting_offset #115358 (azat).
  • Correction de LOGICAL_ERROR: Index with name auto_minmax_index_<column> already exists lorsqu’un même statement ALTER TABLE combinait RENAME COLUMN avec une autre commande, telle que MODIFY SETTING, sur une table dotée d’index minmax implicites (add_minmax_index_for_numeric_columns et paramètres associés). Par ailleurs, le renommage d’une colonne ordinaire ne fait plus échouer un DROP COLUMN ultérieur avec UNKNOWN_IDENTIFIER, et ne prive plus un ADD COLUMN ultérieur de son index implicite. #116063 (groeneai).
  • Un motif LIKE/NOT LIKE sans jokers (%, _) utilise désormais une plage exacte de clé primaire : il lit donc le même nombre de granules que le prédicat =/!= équivalent, au lieu d’une plage de préfixe plus large. #107077 (groeneai).
  • Correction d’une LOGICAL_ERROR (Invalid binary search result in MergeTreeSetIndex) et d’un élagage d’index manqué dans les builds de production lorsqu’une condition IN/NOT IN sur la clé primaire encapsule la clé dans un intDiv par une constante et que la clé est un entier non signé dont les valeurs franchissent la limite signée du type de résultat de intDiv (par exemple intDiv(uint64_column, -9223372036854775807)). #107586 (groeneai).
  • Correction de numericIndexedVectorPointwiseMultiply, qui renvoyait un résultat vide lors de la multiplication par un vecteur composé uniquement de uns ayant une configuration de bits BSI différente. #108027 (alexey-milovidov).
  • Correction d’un heap-buffer-overflow lors de la construction d’un index de texte avec positions = 1 sur de nombreux jetons courts distincts. #108659 (groeneai).
  • Les expressions preprocessor et postprocessor d’un index de texte peuvent désormais référencer des colonnes ALIAS. #110014 (Ergus).
  • L’optimisation de la recherche vectorielle est désormais ignorée lorsque LIMIT ... WITH TIES est utilisé : cette optimisation limite la recherche ANN à exactement n candidats, ce qui entraîne la suppression des lignes à égalité avec la n-ième ligne. #110453 (tamish560).
  • Correction de la non-utilisation de l’index de texte pour ILIKE lorsque l’index est défini sur une expression plutôt que sur une simple colonne (par exemple assumeNotNull(col) avec un préprocesseur lower(...)). Ces requêtes utilisent désormais l’index au lieu de lire l’ensemble des granules. #110595 (ahmadov).
  • Correction d’une erreur LOGICAL_ERROR (LazyMaterializingTransform: Number of rows in lazy chunk N does not match number of offsets M) qui pouvait survenir dans les requêtes de recherche vectorielle utilisant vector_search_with_rescoring = 1 conjointement avec la matérialisation différée, lorsque plusieurs distances étaient à égalité. #111003 (groeneai).
  • Rejet des vecteurs dont la norme au carré provoque un dépassement de capacité vers l’infini lors de l’utilisation d’un index vector_similarity avec une quantification i8 (auparavant, ces vecteurs déclenchaient un comportement indéfini dans usearch et produisaient silencieusement des résultats incohérents). #111085 (groeneai).
  • Correction de lectures obsolètes sur les disques plain_rewritable lorsque le cache de pages est activé. #111105 (Michicosun).
  • Correction de résultats count() erronés et de lignes perdues lorsqu’une clé ou un index de saut minmax de type String (ou FixedString plus étroit) est filtré par une comparaison avec une constante FixedString plus large, par exemple toFixedString('abc', 257) = string_col. L’élagage par clé primaire et par minmax construisait la plage à partir de la constante complétée par des caractères NUL, et ignorait donc à tort des granules correspondants. #111106 (groeneai).
  • Correction de la lecture des fichiers Parquet dont l’index des offsets ne commence pas à la ligne 0. Un tel fichier pouvait amener le lecteur Parquet natif à renvoyer des lignes erronées, ou à signaler LOGICAL_ERROR au lieu de INCORRECT_DATA. L’index des offsets est désormais validé au moment de sa lecture. #114530 (groeneai).

Correctifs liés à la sécurité, à l’accès, à la sauvegarde et à la restauration

  • RESTORE effectue désormais un fsync des fichiers des parties de données restaurées lorsque fsync_after_insert est activé sur la table de destination, de sorte qu’une partie de données restaurée résiste aussi bien à une coupure de courant qu’une partie insérée. Auparavant, les fichiers restaurés étaient seulement écrits puis fermés (sans aucun fsync) : une coupure de courant survenant juste après que RESTORE a renvoyé RESTORED pouvait donc laisser des parties de données corrompues et une table vide. #111378 (groeneai).
  • Correction d’un problème de sécurité qui permettait à un client TCP non authentifié de sonder l’existence de tables et l’état de la réplication via le port interserveur. #99854 (tiandiwonder).
  • Correction de DELETE FROM (lightweight delete), qui exigeait le privilège ALTER UPDATE en plus de ALTER DELETE. Un utilisateur disposant uniquement du grant ALTER DELETE peut désormais exécuter DELETE FROM, conformément à la documentation. #107491 (tiandiwonder).
  • Correction de CREATE TABLE ... AS SELECT sur les bases de données Atomic, qui laissait derrière lui une table vide en cas d’échec de la requête — par exemple lorsque l’utilisateur n’a pas accès à une table référencée dans une sous-requête. Auparavant, une nouvelle tentative signalait que la table existait déjà au lieu de renvoyer l’erreur d’origine. La table est désormais créée via une table temporaire et ne devient visible qu’une fois entièrement remplie. #108048 (alexey-milovidov).
  • Correction d’une LOGICAL_ERROR (No available columns) lors de l’exécution de SELECT count() (ou d’autres requêtes triviales) sur une table pour laquelle l’utilisateur ne dispose de l’accès SELECT que sur une colonne ALIAS. #108056 (alexey-milovidov).
  • Correction d’un problème qui permettait à des requêtes non authentifiées d’amener ClickHouse à résoudre des noms d’hôte fournis dans les en-têtes d’adresse client transférée. ClickHouse n’accepte désormais que des adresses IP numériques dans ces en-têtes. Les valeurs invalides sont ignorées, sans résolution DNS, pour l’attribution des quotas par adresse transférée. Lorsque auth_use_forwarded_address est activé, les adresses transférées invalides sont rejetées lors de l’authentification. Les rejets sont journalisés au niveau debug. #111060 (otselnik).
  • Correction de SQL SECURITY DEFINER (et SQL SECURITY NONE), qui n’était pas respecté pour les vues paramétrées lorsque l’analyseur est désactivé (enable_analyzer = 0). Auparavant, une sélection sur une telle vue par un utilisateur sans privilèges sur la table sous-jacente échouait avec ACCESS_DENIED, alors que la même vue fonctionne lorsque l’analyseur est activé. #111458 (groeneai).
  • Correction d’un défaut dans les intégrations MySQL : le remplacement d’un identifiant TLS d’une collection nommée par une valeur ssl_ca_pem/ssl_cert_pem/ssl_key_pem vide était accepté lorsque la collection stockait l’identifiant sous forme de contenu, ce qui supprimait silencieusement le CA ou le certificat client configuré au lieu de rejeter le remplacement. #113947 (alexey-milovidov).
  • Correction de EXPLAIN ANALYZE, qui perdait les statistiques de jointure d’une vue déclarée SQL SECURITY DEFINER ou SQL SECURITY NONE dont le corps contient un JOIN. Dans les builds debug et sanitizer, la requête s’interrompait avec l’erreur logique JoinStep analyzed without the analyze mode. #115156 (groeneai).
  • Correction de RESTORE ... AS <new name>, qui laissait la liste REFRESH ... DEPENDS ON d’une vue matérialisée actualisable pointer vers la base de données d’origine. La vue restaurée se rafraîchissait en réaction à une table qu’elle ne lisait plus, ignorait les rafraîchissements de son propre parent et cessait complètement de se rafraîchir dès que la base de données d’origine était supprimée. #115689 (groeneai).
  • Correction d’une exception LOGICAL_ERROR lors de la sauvegarde d’une base de données Replicated supprimée et recréée simultanément ; ces sauvegardes échouent désormais proprement avec CANNOT_GET_REPLICATED_DATABASE_SNAPSHOT. #100651 (alexey-milovidov).
  • GRANT role TO role appliqué à un même rôle est désormais rejeté avec BAD_ARGUMENTS, au lieu de créer silencieusement une entrée autoréférente dans system.role_grants. #103315 (zxuhan).
  • Le BACKUP d’une base de données MaterializedPostgreSQL ne reste plus bloqué indéfiniment avec le message « Table … were created or changed its definition during scanning » : il sauvegarde désormais réellement les données de la table (en déléguant au ReplacingMergeTree sous-jacent), qui peuvent être restaurées sous forme de ReplacingMergeTree autonome. #107433 (alexey-milovidov).
  • EXISTS <dictionary> est désormais autorisé pour un utilisateur disposant uniquement du privilège SHOW DICTIONARIES sur le dictionnaire (auparavant, SHOW TABLES était requis). #108084 (alexey-milovidov).
  • Correction du nettoyage d’un BACKUP ... TO Memory(...) qui échoue avant sa finalisation : la sauvegarde en échec restait enregistrée, ce qui empêchait de réutiliser son nom. #109947 (jkartseva).
  • Correction d’une erreur logique ReadBuffer is canceled. Can't read from it. (arrêt brutal du serveur dans les constructions debug/sanitizer) pouvant survenir lors de la lecture d’une archive zip (par exemple pendant un RESTORE depuis une sauvegarde zip) lorsqu’une lecture précédente de la même archive avait échoué en cours de flux. #110197 (groeneai).
  • Correction d’une corruption du tas, susceptible de faire planter le serveur lors de son arrêt, causée par la méthode const AccessRights::getFilters, qui modifiait un arbre de droits d’accès partagé par des requêtes concurrentes. #112692 (groeneai).

Exécution distribuée, tables, vues et dictionnaires

  • Correction d’une erreur Inconsistent AST formatting injustifiée pour ALTER TABLE ... MOVE PART ... TO SHARD '<path>'. Le formateur de requêtes omettait le mot-clé SHARD, si bien que la requête reformatée ne pouvait plus être analysée, ce qui provoquait une LOGICAL_ERROR (une exception gérée dans les builds de production, un arrêt brutal dans les builds de débogage et avec sanitizers). #109060 (groeneai).
  • Correction d’une erreur logique (!part.empty()) lorsqu’une table Distributed définie avec un nom de base de données distante vide est utilisée dans un JOIN avec distributed_product_mode = 'local'. #110677 (groeneai).
  • Correction de DISTINCT avec un LIMIT/OFFSET ordonné, qui renvoyait des lignes incorrectes lorsque l’indice de limite d’arrêt anticipé de DISTINCT ne pouvait pas borner le début du résultat : un LIMIT négatif (le début était renvoyé au lieu de la fin), un LIMIT/OFFSET fractionnaire (la fraction n’est résolue qu’après une lecture complète) et un OFFSET seul sans LIMIT (la fin située après la position était ignorée). Par exemple, SELECT DISTINCT intDiv(x, 100) FROM t ORDER BY intDiv(x, 100) LIMIT -1 sur une table comportant plusieurs parties de données renvoyait la plus petite valeur distincte au lieu de la plus grande. #111326 (groeneai).
  • Correction du cas où un alias placé après une sous-requête dans DESCRIBE TABLE était refusé par le parseur et provoquait une erreur de syntaxe. Corrige https://github.com/ClickHouse/ClickHouse/issues/100031. #100205 (yariks5s).
  • Correction d’un arrêt brutal du serveur (Logical error: 'Block structure mismatch') pouvant survenir lors d’un INSERT concurrent pendant l’exécution d’un ALTER RENAME COLUMN dans une base de données Atomic avec lazy_load_tables = 1 après un DETACH DATABASE / ATTACH DATABASE. #104852 (groeneai).
  • Correction de la corruption des noms de colonnes et de l’exception BAD_ARGUMENTS provoquées par le paramètre inject_random_order_for_select_without_order_by. Lorsque ce paramètre est activé, les SELECT portant sur plusieurs colonnes n’échouent plus et les noms des colonnes en sortie (par exemple pour CREATE TABLE AS SELECT, CREATE VIEW AS SELECT, JSONEachRow) sont conservés au lieu d’être remplacés par des alias internes __subquery_column_<UUID>. #105896 (groeneai).
  • Correction d’un indice trompeur « Maybe you meant X? » après un redémarrage du serveur (ou un DETACH/ATTACH DATABASE) : lors de la suppression d’une table déjà supprimée, le nom qui venait d’être supprimé était proposé comme alternative. Les tâches de chargement asynchrone sont désormais retirées de DatabaseOrdinary::startup_table et de load_table lors du détachement. #106238 (groeneai).
  • Correction des moteurs de table et de base de données MaterializedPostgreSQL afin qu’une table ou une base de données isolée puisse être répliquée depuis un schéma PostgreSQL autre que celui par défaut (materialized_postgresql_schema), y compris lorsque des tables portant le même nom existent dans plusieurs schémas de la même base de données (auparavant, elles partageaient une publication et un slot de réplication, et interféraient entre elles). #107425 (alexey-milovidov).
  • Correction de NO_SUCH_COLUMN_IN_TABLE lorsqu’une requête utilisant des répliques parallèles sélectionne une colonne ALIAS d’une table transmise en tant que table temporaire GLOBAL JOIN, de MULTIPLE_EXPRESSIONS_FOR_ALIAS lorsque les deux côtés d’un JOIN distribué déclarent une colonne ALIAS portant le même nom, et de UNKNOWN_IDENTIFIER lorsqu’une table Distributed déclare une colonne ALIAS reposant sur une autre colonne ALIAS, y compris en tant que clé de JOIN USING. #107700 (yakov-olkhovskiy).
  • Correction d’un dépassement de capacité d’entier signé lorsqu’une vue matérialisée actualisable relance un rafraîchissement ayant échoué alors que refresh_retries est défini sur une valeur très élevée (proche du maximum de Int64). Ce dépassement de capacité pouvait provoquer l’arrêt brutal du serveur dans les constructions utilisant le sanitizer de comportement indéfini. #108005 (groeneai).
  • Correction d’une exception (CANNOT_PARSE_TEXT) lors du chargement d’un dictionnaire doté d’une clé composite dont les colonnes de clé ne sont pas les premières colonnes de la définition du dictionnaire, avec une source ClickHouse locale utilisant une requête explicite. Les colonnes sources sont désormais associées à la structure du dictionnaire par leur nom et non plus par leur position. #108053 (alexey-milovidov).
  • Correction d’un plantage (déréférencement de pointeur nul) lors de l’interrogation d’une table Hive sans clause WHERE, par exemple SELECT * FROM hive_table. #108094 (alexey-milovidov).
  • Correction d’un plantage du serveur (erreur de segmentation) pouvant survenir lorsqu’une requête distribuée faisait référence à une CTE MATERIALIZED non encore matérialisée en tant que table externe et que la source distante était créée de manière différée (lazy). Le plantage était dû au déréférencement d’un Logger nul dans RemoteQueryExecutor::sendExternalTables(). #108547 (groeneai).
  • Correction d’une erreur logique (Equal values are not contiguous within the range assumed to be sorted) lors de l’exécution de DISTINCT sur une lecture STREAM (SELECT DISTINCT ... FROM table STREAM). Les optimisations de lecture ordonnée ne sont plus appliquées aux lectures STREAM, qui renvoient les lignes dans l’ordre de validation et non dans l’ordre de la clé de tri. #108568 (groeneai).
  • Correction d’une exception (Cannot find sharding key column, ainsi qu’un arrêt brutal du serveur dans les constructions debug/sanitizer) lorsque la clé de sharding d’une table Distributed est une expression que l’analyseur simplifie par repli de constantes, par exemple if(1, toInt32(id), toInt32(id) + 1), avec optimize_skip_unused_shards = 1. #108737 (groeneai).
  • Correction d’un arrêt brutal du serveur (LOGICAL_ERROR / assertion dans les constructions debug et sanitizer) lorsqu’un INSERT ... SELECT passant par une table Alias est annulé sans exception, par exemple avec timeout_overflow_mode = 'break'. #108783 (groeneai).
  • Correction d’un arrêt brutal du serveur (LOGICAL_ERROR / assertion dans les constructions debug et sanitizer) lorsqu’un INSERT ... SELECT vers une table TimeSeries est annulé sans exception, par exemple avec timeout_overflow_mode = 'break'. #108796 (tiandiwonder).
  • Correction d’échecs occasionnels de requêtes distribuées avec UNEXPECTED_PACKET_FROM_SERVER (“expected TablesStatusResponse, got ProfileInfo”) lorsqu’une connexion laissée désynchronisée par une requête annulée précédemment était réutilisée depuis le pool. #108854 (alexey-milovidov).
  • Correction d’une LOGICAL_ERROR (CTE '...' does not have query tree, but was not planned yet) lorsqu’une CTE MATERIALIZED est référencée à plusieurs endroits de l’arbre de jointure (par exemple dans deux sous-requêtes jointes, ou dans une sous-requête jointe et une sous-requête scalaire dans WHERE) au sein d’une requête portant sur une table Distributed. La requête s’exécute désormais au lieu de lever l’exception (qui provoquait l’arrêt brutal du serveur dans les constructions debug/sanitizer). #108924 (groeneai).
  • Corrige un problème de sécurité mémoire où des tâches asynchrones planifiées lors du traitement d’une vue matérialisée pouvaient continuer à utiliser la comptabilisation au niveau de la requête après la fin de celle-ci. #108988 (filimonov).
  • Corrige une erreur LOGICAL_ERROR (“Cannot find __grouping_set column in header of MergingAggregatedTransform with grouping sets”, ou “Chunk info was not set for chunk in MergingAggregatedTransform”) qui pouvait survenir, sans l’analyseur, pour un UNION ALL/INTERSECT/EXCEPT dont une branche utilise GROUP BY GROUPING SETS avec les répliques parallèles et une autre utilise FINAL. #109003 (groeneai).
  • Corrige l’échec de CREATE TABLE ... AS SELECT FROM s3Cluster(...) (et d’autres fonctions de table de cluster telles que fileCluster/urlCluster) avec l’erreur NOT_FOUND_COLUMN_IN_BLOCK dans une base de données Replicated. #109266 (groeneai).
  • Corrige une erreur (NOT_FOUND_COLUMN_IN_BLOCK, ou std::bad_function_call dans les versions antérieures) lors de l’exécution de CREATE TABLE ... ON CLUSTER ... AS SELECT lisant une table Distributed sur un cluster comportant au moins deux shards. #109407 (alexey-milovidov).
  • Corrige une erreur interne déroutante (Method getResultType is not supported for TABLE query tree node) lorsqu’une expression de table était utilisée comme argument gauche de l’opérateur IN ; ces requêtes produisent désormais un message d’erreur explicite. #109412 (alexey-milovidov).
  • Corrige l’erreur logique Bad cast from type DB::FunctionNode to DB::ConstantNode (arrêt brutal du serveur dans les constructions debug/sanitizer) lors de l’exécution de SELECT ... ORDER BY ... WITH FILL sur une table Distributed avec une valeur faible de optimize_const_name_size. #109938 (groeneai).
  • Corrige une erreur LOGICAL_ERROR (Left and right columns have same names) dans l’optimiseur d’ordre des jointures, qui pouvait être déclenchée par une jointure par virgule sur une vue multi-tables lorsque query_plan_merge_expression_into_join est activé. #110227 (groeneai).
  • Corrige une erreur rare Logical error: 'No more packets are available.' levée pour une requête distribuée valide comportant un LIMIT, qui provoquait l’arrêt brutal du serveur dans les constructions debug et sanitizer. #110334 (groeneai).
  • Corrige un bogue de l’analyseur syntaxique où COMMENT était interprété à tort comme un alias implicite lorsqu’une requête SELECT se terminait directement après un simple identifiant de table (par ex. ... FROM t COMMENT 'x'), ce qui provoquait une erreur de syntaxe trompeuse au lieu d’appliquer le commentaire à la vue ou à la table. #110372 (adityaksolves).
  • Corrige un résultat erroné où length() (ainsi que la dimension QBit) appliqué à une colonne FixedString renvoyait 0 au lieu de la taille fixe N avec les répliques parallèles lorsque la requête comportait un filtre sélectif. #110427 (groeneai).
  • Corrige l’erreur NOT_FOUND_COLUMN_IN_BLOCK pour les requêtes comportant une sous-requête dans FROM lorsque les répliques parallèles s’exécutent en mode custom_key_sampling / custom_key_range. Ces requêtes (par ex. SELECT * FROM (SELECT id, k, v FROM t WHERE id < 20) ORDER BY k) échouaient auparavant avec Not found column __table2.id in block .... #110690 (groeneai).
  • Correction de Logical error: 'clock' (arrêt brutal du serveur dans les builds debug/sanitizer ; blocage infini dans les builds release) lors de l’exécution de EXPLAIN ANALYZE sur une requête comportant une lecture en flux (FROM ... STREAM) qui n’est pas une simple expression de table de premier niveau : imbriquée dans une sous-requête (par exemple dans WHERE ... IN (...) ou une CTE), ou masquée derrière une vue ordinaire ou paramétrée. EXPLAIN ANALYZE rejette désormais ces requêtes avec NOT_IMPLEMENTED, comme il le faisait déjà pour une lecture en flux de premier niveau. #110935 (groeneai).
  • Correction de la résolution des noms de colonne pleinement qualifiés tels que db.table.column dans l’analyseur, pour les tables portant le même nom que leur base de données. #110976 (alexey-milovidov).
  • Correction des erreurs NOT_FOUND_COLUMN_IN_BLOCK / THERE_IS_NO_COLUMN (et, sur certaines versions, de résultats erronés sans avertissement) pour un RIGHT JOIN lorsque parallel_replicas_min_number_of_rows_per_replica est défini et qu’une colonne de la table de gauche est projetée. #111332 (groeneai).
  • Correction d’une condition de concurrence dans les UDF exécutables, le moteur de table executable et les dictionnaires exécutables : un fcntl sur un descripteur de fichier déjà fermé pouvait corrompre un descripteur sans rapport appartenant à une requête concurrente (en supprimant O_NONBLOCK), ce qui pouvait bloquer des requêtes indéfiniment. #111779 (Algunenano).
  • Correction d’une LOGICAL_ERROR (« Invalid number of rows in Chunk ») — et, dans les builds renforcés, d’un arrêt associé dû à un accès hors limites — pour ORDER BY ... WITH FILL ... INTERPOLATE sur une table Distributed comportant au moins deux shards, sur clusterAllReplicas ou sur des répliques parallèles à clé personnalisée, y compris avec des résultats vides. WITH FILL n’est désormais appliqué que sur le nœud qui produit le résultat final, ce qui corrige également la duplication des lignes de remplissage générées entre shards/répliques. #111919 (yakov-olkhovskiy).
  • Correction du blocage indéfini de DROP TABLE ... SYNC lorsqu’une requête sur system.replicas avait été interrompue avant le traitement de ses demandes de statut. #112164 (evillique).
  • Correction d’une exception LOGICAL_ERROR (Block structure mismatch in joined block stream) sur une jointure multi-tables dont les deux sous-jointures ne produisent aucune colonne en sortie, par exemple SELECT count() FROM t1, t2, t3, t4 WHERE (t1.b = t2.b) AND (t3.a = t4.a) avec query_plan_optimize_join_order_limit défini à 0 ou 1. #112205 (groeneai).
  • Correction d’une erreur logique Cannot add step Expression to QueryPlan because it has incompatible header with root step JoinLazyColumnsStep pour une requête avec FINAL, un filtre PREWHERE, un petit LIMIT et make_distributed_plan = 1, lorsque les colonnes sélectionnées sont listées dans un ordre différent de celui des colonnes de la table. La matérialisation différée remplaçait un nœud du plan sans conserver son en-tête de sortie, ce qui rendait le plan de requête incohérent. #112303 (groeneai).
  • Correction de résultats erronés et d’une erreur logique lorsqu’un argument devant être constant, comme l’échelle de toDecimal32 ou le fuseau horaire de toDateTime, provient d’une sous-requête scalaire et que l’ancien analyseur est utilisé. Un tel argument était analysé comme si sa valeur était nulle ou vide : le type de résultat déclaré ne correspondait donc pas à la valeur calculée, et CREATE VIEW et CREATE MATERIALIZED VIEW enregistraient un type de colonne erroné. #112492 (groeneai).
  • Corrige une erreur logique et un enregistrement de dépendance erroné lorsque le corps d’une VIEW ordinaire référence une table ou un dictionnaire via un paramètre de requête, comme dans CREATE VIEW v AS SELECT x FROM {db:Identifier}.t. Une telle vue n’enregistre plus de dépendance référentielle envers une table homonyme de la base de données courante : des tables sans rapport peuvent donc de nouveau être supprimées. #112704 (groeneai).
  • Corrige une erreur logique lorsqu’une table utilisant le table engine Join se trouve du côté droit d’une JOIN dont la section ON contient une condition portant sur des colonnes des deux tables, par exemple ON (t.key = j.key) AND (t.a < j.a). Une telle requête est désormais rejetée avec une exception explicite, au lieu de lever required columns: ... but not found any in left table ou std::bad_variant_access. #112815 (groeneai).
  • Correction d’un comportement indéfini et d’une erreur de planification silencieuse lorsqu’une tâche en arrière-plan est planifiée avec un délai très long, par exemple par une vue matérialisée actualisable dotée d’une période REFRESH AFTER démesurée. Les délais sont désormais plafonnés à la plus grande valeur représentable au lieu de provoquer un dépassement de capacité. #113041 (groeneai).
  • Correction de l’erreur signalée lorsqu’un INSERT asynchrone dans une table Distributed nécessite un répertoire de file d’attente dont le nom dépasse la limite de 255 octets du système de fichiers (cas possible avec use_compact_format_in_distributed_parts_names = 0). Il s’agissait d’une LOGICAL_ERROR pour un shard avec internal_replication, et d’une Code: 1001. std::exception sans origine identifiée dans les autres cas ; les deux cas signalent désormais ARGUMENT_OUT_OF_BOUND en indiquant la table, le cluster et la limite. #113083 (groeneai).
  • UNDROP TABLE peut désormais être interrompu par KILL QUERY pendant qu’il attend que les requêtes en cours libèrent la table supprimée. #113263 (alexey-milovidov).
  • Correction d’un blocage permanent de DROP TABLE et d’une fuite de mémoire après l’échec d’une requête utilisant une CTE matérialisée. La table restait bloquée dans la file d’attente de suppression jusqu’au redémarrage du serveur. #113397 (tiandiwonder).
  • Corrige une erreur de segmentation et une LOGICAL_ERROR (No host found for exchange stream final_result__0_0) lorsqu’une sous-requête définit distributed_plan_execute_locally dans sa propre clause SETTINGS avec make_distributed_plan. Le plan distribué et le code chargé de l’exécuter lisaient le paramètre dans deux contextes différents et pouvaient diverger, si bien que l’initiateur construisait un pipeline incohérent. #113422 (groeneai).
  • Correction d’un CREATE TABLE ... AS SELECT qui devenait impossible à interrompre après l’échec de son SELECT de remplissage lorsque database_atomic_wait_for_drop_and_detach_synchronously est activé : le DROP interne de nettoyage de la table temporaire attendait la file d’attente de suppression en arrière-plan et ignorait à la fois KILL QUERY et max_execution_time. #113504 (groeneai).
  • Correction de résultats erronés et, dans une version compilée avec les assertions activées, d’un échec d’assertion pour une JOIN sur un dictionnaire dont la clause ON comporte une condition de non-égalité portant sur les deux tables, telle que ON (t.key = d.key) AND (t.a * 10 < d.a), lorsque join_use_nulls est activé. La condition était évaluée sur une colonne lue avec un type incorrect, si bien que des lignes pouvaient correspondre de manière arbitraire. #113534 (alexey-milovidov).
  • Correction d’un problème où une vue paramétrée perdait sa base de données lorsqu’une requête y faisant référence sans qualification était envoyée à un shard, si bien que celui-ci résolvait le nom par rapport à sa propre base de données par défaut. Selon le contenu de cette base de données, la requête échouait avec UNKNOWN_FUNCTION ou renvoyait silencieusement des lignes provenant d’une autre vue. Concerne IN, JOIN et remote() via une table Distributed. #113550 (groeneai).
  • Correction de l’erreur Unsupported JOIN keys of type keys256 in StorageJoin lors de la lecture d’une table utilisant le moteur Join dont la clé est composite (plusieurs colonnes) ou constituée d’une seule valeur de 16/32 octets telle que UUID, Int256 ou FixedString(4). Une telle table pouvait être créée, alimentée et utilisée dans une jointure, mais jamais relue avec SELECT. #113578 (groeneai).
  • Correction d’une erreur logique Sending a distributed query with unknown (zero) client version lorsqu’une table Distributed sans structure portant sur un shard distant est attachée ou chargée à partir de métadonnées écrites par une version plus ancienne du serveur. #113675 (alexey-milovidov).
  • Correction d’une erreur LOGICAL_ERROR lorsqu’un SELECT utilisait le modificateur STREAM sur une table nommée conjointement avec les répliques parallèles en mode read-tasks. Avec une CTE matérialisée utilisée comme set IN dans PREWHERE, le serveur levait Reading from materialized CTE ... DelayedPortsProcessor gate is missing in the query plan (provoquant un arrêt brutal du serveur dans les builds debug et sanitizer). Une table portant STREAM est désormais rejetée lorsque les répliques parallèles sont demandées : avec enable_parallel_replicas = 2, la requête échoue avec SUPPORT_IS_DISABLED ; avec 1, elle s’exécute sans elles, exactement comme c’est déjà le cas pour FINAL. #113754 (groeneai).
  • Correction de la complexité quadratique de l’analyse de l’argument d’adresse des fonctions de table remote, mysql et similaires : une adresse très longue (par exemple une FixedString d’un mégaoctet complétée par des zéros) bloquait la requête pendant l’analyse, sans possibilité de l’annuler. #113918 (alexey-milovidov).
  • Correction d’une erreur intempestive Failed to drop temporary table after refresh. Table ... is left behind and requires manual cleanup. journalisée lorsque le rafraîchissement d’une vue matérialisée actualisable est annulé (par exemple lors de l’arrêt du serveur) alors que database_atomic_wait_for_drop_and_detach_synchronously est activé. La table temporaire était en réalité bien supprimée ; aucun nettoyage manuel n’a donc jamais été nécessaire. #113957 (groeneai).
  • Correction d’une ligne manquante dans system.query_views_log pour une vue matérialisée qui échoue pendant la collecte de ses dépendances lorsque materialized_views_ignore_errors est activé. Dans les builds debug et sanitizer, cela provoquait également l’arrêt brutal du serveur avec std::out_of_range au lieu de journaliser la vue. #114064 (groeneai).
  • Correction de résultats incohérents lorsqu’un filtre sur une colonne non clé de la table de droite était poussé sous une ANY INNER JOIN. #114313 (kirillshokhin).
  • Correction de CREATE OR REPLACE qui laissait subsister une table interne _tmp_replace_*, et de CREATE OR REPLACE VIEW qui échouait avec NOT_IMPLEMENTED, lorsque ignore_drop_queries_probability est défini. Les DROP émis en interne n’étant que des étapes d’une même instruction utilisateur, l’injection de fautes sur les DROP ne s’y applique plus. #114420 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR lors de la lecture d’une table Delta Lake dont metaData.partitionColumns référence une colonne non déclarée dans metaData.schemaString. Ces métadonnées sont désormais rejetées avec BAD_ARGUMENTS par le lecteur delta-kernel et avec INCORRECT_DATA par l’ancien lecteur, dès que la table ou son schéma est lu à partir d’un instantané, et pas uniquement lorsque la requête comporte un prédicat. #114538 (groeneai).
  • Correction d’un problème où les statistiques des tables de hachage — et, avec elles, la préallocation des tables de hachage d’agrégation — étaient désactivées silencieusement et définitivement pour l’ensemble du serveur lorsqu’une requête utilisant l’optimisation FINAL différée (query_plan_optimize_lazy_final) était la première agrégation exécutée après le démarrage du serveur. #114597 (alexey-milovidov).
  • Correction d’un problème où une vue matérialisée actualisable laissait fuiter son ancienne table cible, remplacée lors de la rotation, lorsque ignore_drop_queries_probability est activé. Le DROP émis par un rafraîchissement pour nettoyer la cible précédente fait partie du rafraîchissement et n’est pas un DROP demandé par l’utilisateur ; l’injection de fautes ne s’y applique donc plus. #114622 (groeneai).
  • Correction de ANY RIGHT JOIN et SEMI RIGHT JOIN comportant plusieurs conditions combinées par OR dans la section ON, qui renvoyaient certaines lignes de la table de droite en double et perdaient les correspondances d’autres lignes. Un ANY LEFT JOIN écrit explicitement était également concerné, car le planificateur peut inverser les tables et l’exécuter comme un ANY RIGHT JOIN. #114676 (vdimir).
  • Correction d’une erreur LOGICAL_ERROR (Cannot find input column ... on its position in inputs of expression actions DAG) lorsqu’une requête comporte un JOIN au-dessus d’une table dont la colonne dotée d’un index textuel est utilisée à la fois par un prédicat de recherche textuelle PREWHERE et par un prédicat WHERE. #114771 (groeneai).
  • Correction de l’erreur NOT_FOUND_COLUMN_IN_BLOCK survenant à chaque INSERT dans une table déclarant STATISTICS(...) sur une colonne ALIAS ou EPHEMERAL, ce qui rendait de fait ces tables accessibles en lecture seule. #115231 (groeneai).
  • Correction de dictGet et de ses variantes dans les requêtes distribuées : un nom de dictionnaire non qualifié est désormais lié à la base de données courante de l’initiateur, au lieu d’être résolu par rapport à la base de données courante de chaque shard, où il était soit introuvable, soit résolu vers un autre dictionnaire portant le même nom. #115541 (alexey-milovidov).
  • Correction d’un bug où la restauration d’une base de données ou d’une table sous un nouveau nom supprimait les alias de tables du corps des vues stockées, si bien que la lecture d’une vue restaurée échouait avec UNKNOWN_IDENTIFIER. #115645 (groeneai).
  • Correction de l’erreur LOGICAL_ERROR “Reading from materialized CTE ‘X’ before its materialization completed - DelayedPortsProcessor gate is missing in the query plan” lorsqu’une CTE MATERIALIZED est lue depuis une sous-requête IN portant sur une table Distributed. #115740 (groeneai).
  • Correction de l’erreur BAD_ARGUMENTS “Dictionary not found” sur dictGet après un DETACH DICTIONARY … PERMANENTLY rejeté par la vérification des dépendances (HAVE_DEPENDENT_OBJECTS). Le dictionnaire reste désormais pleinement utilisable lorsque le détachement est rejeté. #105259 (groeneai).
  • Correction d’une erreur Logical error: 'removed' (arrêt du serveur dans les builds de débogage) dans la file d’attente de suppression de tables en arrière-plan. Elle pouvait se produire lorsque le même UUID explicite était réutilisé dans plusieurs requêtes CREATE OR REPLACE TABLE, ce qui plaçait dans la file d’attente plusieurs tables supprimées partageant cet UUID. #107031 (groeneai).
  • Correction d’un problème où MaterializedPostgreSQL interrompait la réplication d’une base de données entière (avec LOGICAL_ERROR: Columns number mismatch) lorsque la structure d’une seule table répliquée avait changé pendant l’arrêt du serveur. Désormais, seule la table concernée est ignorée ; elle peut être récupérée avec DETACH/ATTACH. #107427 (alexey-milovidov).
  • Correction d’un fort déséquilibre dans la répartition du travail avec les répliques parallèles lorsque le cluster contient des répliques inactives (par exemple, des entrées obsolètes laissées après une mise à l’échelle automatique). Ces répliques ne sont plus prises en compte par le coordinateur de lecture : le travail est ainsi réparti entre les répliques en ligne au lieu de s’accumuler sur une seule. #107805 (alexey-milovidov).
  • Correction d’un blocage de SYSTEM RELOAD DICTIONARIES (et de RELOAD DICTIONARY) lorsque de nombreux dictionnaires MySQL définis en XML partageaient un même pool de connexions via share_connection. Ces pools respectent désormais les paramètres connection_pool_size et connection_wait_timeout (5 secondes par défaut) de la configuration, comme les dictionnaires définis au moyen de collections nommées. #108083 (alexey-milovidov).
  • L’épuisement des tentatives pour le DDL distribué répliqué exécuté par le leader ne bloque plus définitivement les requêtes DDL ON CLUSTER suivantes. #108505 (ahaanlimaye).
  • Correction d’un plantage possible (use-after-free) lorsque SYSTEM FLUSH DISTRIBUTED s’exécute en même temps qu’un DROP TABLE sur la même table Distributed. #108684 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (local_replica_plan_reading_step->getAnalyzedResult() == nullptr) dans le planificateur automatique des répliques parallèles, qui pouvait survenir lorsque automatic_parallel_replicas_mode était activé et que parallel_replicas_min_number_of_rows_per_replica avait une valeur supérieure à 0. #109011 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (creation_csn is not set while removal_csn is set to 1) qui pouvait être levée lorsqu’un TRUNCATE non transactionnel s’exécutait en même temps qu’une transaction non validée ayant inséré des données dans la même table. Un tel TRUNCATE ne supprime plus les parts créées par des transactions pas encore validées. #109598 (tuanpach).
  • Les moteurs de base de données externes (par ex. PostgreSQL) ne délèguent plus les comparaisons de plage (>=, >, <=, <) sur les colonnes UUID. ClickHouse et les bases de données externes n’ordonnent pas les UUID de la même manière : déléguer ces prédicats supprimait donc silencieusement des lignes du résultat. Ils sont désormais évalués par ClickHouse. #109833 (vismaytiwari).
  • Correction d’un plantage du serveur dans le moteur de base de données MaterializedPostgreSQL, qui pouvait se produire lorsqu’une table était détachée (ou que sa structure changeait) alors qu’elle était encore en file d’attente de synchronisation. #110596 (alexey-milovidov).
  • Correction de la réécriture du type d’une colonne EPHEMERAL par les query_masking_rules du serveur. Une règle correspondant à une partie du type amenait CREATE TABLE à enregistrer un type incorrect, voire à échouer. #112048 (alexey-milovidov).
  • Corrige un dépassement inférieur non signé dans la vérification de la longueur du nom de table, qui permettait de créer une table impossible à supprimer. Lorsque le nom de base de données échappé atteignait 214 octets, la limite calculée pour le nom de fichier metadata_dropped repartait au début de la plage, si bien que CREATE TABLE était accepté, tandis que les instructions DROP TABLE ou DROP DATABASE ultérieures échouaient avec File name too long. Un tel nom est désormais rejeté avec ARGUMENT_OUT_OF_BOUND, la même erreur que celle déjà renvoyée juste en dessous de cette limite. #112527 (groeneai).
  • Correction de RENAME TABLE, qui vérifiait la longueur maximale du nom de table par rapport à la base de données source plutôt qu’à la base de données de destination. Renommer une table vers une base de données au nom long pouvait produire une table que DROP TABLE ne parvenait pas à supprimer, tandis que renommer une table pour la sortir d’une telle base de données était rejeté à tort. #113019 (groeneai).
  • Corrige la colonne views de query_log, qui restait toujours vide lors des insertions dans une table Alias dont la cible déclenche des vues matérialisées. #113076 (eclbg).
  • Les expressions parallel_replicas_custom_key invalides, telles que tuple() ou (x, y), sont désormais rejetées avec ILLEGAL_TYPE_OF_COLUMN_FOR_FILTER au lieu de provoquer une lecture au-delà de la liste de types de la clé personnalisée. #113359 (nickitat).
  • Correction de RENAME DATABASE, qui acceptait un nom cible suffisamment long pour rendre les tables de la base de données impossibles à supprimer. La vérification de la longueur du nom de table s’exécute désormais indépendamment de check_table_dependencies et couvre également les tables détachées, qui n’étaient jamais vérifiées et étaient donc concernées même avec les paramètres par défaut. #113557 (groeneai).
  • Lors d’insertions en arrière-plan dans des tables utilisant le moteur Distributed, les délais provoqués par des erreurs répétées lors de l’envoi de données vers des shards distants diminuent désormais une fois les erreurs résolues. Auparavant, le délai ne faisait qu’augmenter, sans jamais être réinitialisé. #87378 (Blackmorse).
  • Rejet des poignées de main du protocole natif qui utilisent USER_INTERSERVER_MARKER pour un cluster inconnu ou pour un cluster configuré sans <secret>. Auparavant, une telle poignée de main permettait à un client non authentifié de passer en mode interserveur et d’utiliser des paquets de protocole antérieurs à l’authentification (par ex. TablesStatusRequest) s’appuyant sur un fake_interserver_context, et ainsi d’obtenir des informations sur les tables locales sans avoir à prouver qu’il connaissait le secret de cluster. #99675 (MillaFleurs).

Corrections liées à SQL, à l’analyseur et aux jointures

  • Correction de la substitution des paramètres de requête dans les définitions d’une clause WINDOW nommée. Auparavant, ces paramètres n’étaient pas substitués, sans aucun avertissement, et un paramètre Identifier non défini pouvait provoquer l’exception Logical error: '!part.empty()' lors de EXPLAIN SYNTAX. #110506 (alexey-milovidov).
  • Correction d’un plantage rare du serveur lorsque des vues matérialisées actualisables sont supprimées ou remplacées de manière concurrente. Un pointeur nul était déréférencé dans RefreshTask::doScheduling lorsqu’un autre thread affectait nullptr à la view de la tâche alors que le code de planification avait temporairement libéré son mutex. #105588 (groeneai).
  • Correction d’un résultat erroné renvoyé par une vue définie avec EXCEPT ou INTERSECT dont l’opérande est une chaîne UNION, après un DETACH/ATTACH ou un redémarrage du serveur. Le formateur omettait les parenthèses autour des enfants UNION de INTERSECT/EXCEPT ; le SQL stocké était donc réanalysé avec une priorité inversée. #105935 (groeneai).
  • Correction de résultats erronés (lignes dupliquées) pour SELECT DISTINCT appliqué à un GROUP BY avec WITH CUBE, WITH ROLLUP ou GROUPING SETS sur les mêmes clés. L’optimisation query_plan_remove_redundant_distinct ne supprime plus le DISTINCT dans ces cas, car les modificateurs de regroupement produisent des lignes supplémentaires dont les colonnes clés prennent leur valeur par défaut, lesquelles peuvent entrer en collision avec de véritables valeurs de groupe. #107072 (groeneai).
  • Correction d’un résultat erroné dans un OUTER JOIN lorsque le filtre WHERE est une conjonction contenant un terme constant (par exemple p AND 1, ou WHERE p QUALIFY NULL où QUALIFY NULL est fusionné dans WHERE). Le terme constant pouvait être poussé vers le côté non préservé de la jointure et retiré du filtre post-jointure : la jointure produisait alors des lignes sans correspondance dont les colonnes de ce côté prenaient leur valeur par défaut, et ces lignes passaient à tort le filtre. #107084 (groeneai).
  • Correction d’une LOGICAL_ERROR (“Cannot find input column … on its position in inputs of expression actions DAG”) qui interrompait le serveur pour une sous-requête scalaire corrélée portant sur une source projetant deux fois le même identifiant de colonne (par ex. SELECT number, *) lorsque correlated_subqueries_default_join_kind = 'left'. #107112 (groeneai).
  • Correction d’une LOGICAL_ERROR (arrêt du serveur dans les builds debug/sanitizer) lorsqu’un JOIN utilise l’opérateur de comparaison null-safe (<=> / IS NOT DISTINCT FROM) et que l’une des clés est une sous-requête scalaire, par ex. ... JOIN t2 ON (SELECT x FROM t1) <=> t2.k, avec l’ancien analyseur (enable_analyzer = 0). Une telle requête renvoie désormais une erreur NOT_FOUND_COLUMN_IN_BLOCK classique au lieu d’une erreur logique. #108123 (groeneai).
  • Correction des exceptions LOGICAL_ERROR (Join is supported only for pipelines with one output port) et NOT_IMPLEMENTED (MergeJoinAlgorithm is not implemented for strictness Semi) lorsque join_algorithm = 'full_sorting_merge' était utilisé pour une jointure SEMI/ANTI, y compris les jointures issues de la décorrélation d’une sous-requête corrélée EXISTS. full_sorting_merge refuse désormais les combinaisons strictness/kind qu’il ne peut pas exécuter, de sorte que le planificateur se rabat sur un autre algorithme activé ou signale que la jointure ne peut pas être exécutée. #108658 (groeneai).
  • Correction d’un bogue où la ligne WITH TOTALS d’un JOIN pouvait contenir des valeurs par défaut (comme 0) au lieu des valeurs constantes issues d’une sous-requête constante située d’un côté de la jointure. Le résultat erroné n’apparaissait qu’avec certains ordres de jointure ou avec le paramètre query_plan_join_swap_table. #108807 (vdimir).
  • Correction d’un arrêt brutal au démarrage (Cannot allocate ThreadStack, EINVAL) sur les constructions glibc exécutées sur des CPU dotés d’une pile de signaux de grande taille (par exemple Intel Sapphire Rapids / Granite Rapids avec un noyau prenant en charge AMX) : la taille de la pile alternative de signaux n’était pas arrondie au multiple supérieur de la taille de page avant l’appel à aligned_alloc. #108813 (groeneai).
  • Correction d’une erreur logique Block structure mismatch in JoinStep (arrêt du serveur dans les constructions debug/sanitizer) lorsqu’une sous-requête corrélée est décorrélée en jointure et que la relation source contient plusieurs fois le même nom de colonne, par exemple WITH t AS (SELECT number, * FROM numbers(3)) SELECT *, (SELECT t.number WHERE t.number >= 0) FROM t avec correlated_subqueries_default_join_kind = 'right'. #109114 (groeneai).
  • Correction de CREATE USER ... HOST LIKE 'a', 'b' (et de son équivalent ALTER USER), qui ne conservait silencieusement que le premier motif. Tous les motifs HOST LIKE spécifiés sont désormais enregistrés dans la liste des hôtes autorisés de l’utilisateur. #109187 (groeneai).
  • Correction d’un résultat inattendu pour ANY JOIN avec une condition ON constante (par exemple t1 ANY INNER JOIN t2 ON 1) : la requête renvoyait le produit cartésien complet au lieu du résultat de la jointure ANY #109207 (vdimir).
  • Correction de la limite globale max_rows_in_join / max_bytes_in_join, qui n’était pas appliquée avec l’algorithme de jointure parallel_hash : une requête avec join_overflow_mode = 'throw' pouvait ainsi réussir silencieusement au lieu de lever SET_SIZE_LIMIT_EXCEEDED. #109488 (m-selmi).
  • Correction d’une perte de données silencieuse lorsqu’un INSERT ... VALUES dans un flux multi-requêtes est suivi d’un commentaire SQL final (par exemple VALUES (1) -- comment) avec l’analyse côté serveur des insertions à données intégrées (send_table_structure_on_insert_with_inline_data = 0). Le commentaire final était analysé comme des données de ligne au-delà du ; terminal, si bien que les requêtes suivantes du flux étaient ignorées sans avertissement. #109643 (groeneai).
  • Correction du PREWHERE différé avec optimize_move_to_prewhere = 1 lorsque PREWHERE s’exécute après FINAL : les conditions WHERE ne sont plus placées avant une politique de lignes différée, ce qui pouvait modifier le résultat des requêtes. #109703 (yariks5s).
  • Correction d’une erreur LOGICAL_ERROR (query tree node does not have valid source node) lorsqu’une CTE récursive résolvait un identifiant d’une portée externe comme une colonne corrélée (avec allow_experimental_correlated_subqueries = 1). Ces requêtes renvoient désormais une erreur explicite UNSUPPORTED_METHOD. #109863 (groeneai).
  • Correction d’un dépassement de capacité d’entier signé lorsque ORDER BY ... WITH FILL franchit un très grand intervalle manquant sur une colonne Int64/UInt64 (par exemple un pas de 2 sur un intervalle proche de 2^63). Ce dépassement de capacité constituait un comportement indéfini détecté par les sanitizers et provoquait un rebouclage silencieux dans les constructions de production. #109937 (groeneai).
  • Correction d’une erreur logique Invalid number of rows in Chunk lorsqu’une cible INTERPOLATE est un alias d’une colonne WITH FILL avec l’ancien analyseur (enable_analyzer = 0). Ces requêtes sont désormais rejetées avec une erreur explicite INVALID_WITH_FILL_EXPRESSION. #110103 (groeneai).
  • Correction d’une erreur logique (Unexpected number of columns in result sample block) lorsqu’un filtre contenant une sous-requête corrélée (par exemple exists((SELECT ...))) est optimisé avec convert_query_to_cnf ou optimize_and_compare_chain. #110187 (alexey-milovidov).
  • Une sous-requête EXISTS corrélée imbriquée qui référence une colonne d’une portée située au-delà de sa requête externe immédiate (en sautant une portée intermédiaire) échoue désormais avec une erreur explicite NOT_IMPLEMENTED au lieu d’une erreur interne NOT_FOUND_COLUMN_IN_BLOCK. #110310 (groeneai).
  • Correction de résultats erronés renvoyés par les sous-requêtes scalaires corrélées lorsque la requête externe contient des valeurs en double pour la colonne corrélée et que la sous-requête est décorrélée via le chemin CROSS JOIN. Auparavant, les agrégats tels que count() et sum() étaient multipliés par le nombre de doublons. Ce problème concerne la fonctionnalité expérimentale allow_experimental_correlated_subqueries. #110311 (groeneai).
  • Correction de ORDER BY ... WITH FILL, qui ne respectait pas max_execution_time et tardait à s’annuler lors de la génération d’une vaste plage de remplissage (en particulier avec INTERPOLATE). #110332 (alexey-milovidov).
  • L’optimisation FINAL différée (query_plan_optimize_lazy_final, désactivée par défaut) n’est plus appliquée lorsque la requête lit dans l’ordre de la clé de tri : son plan de remplacement ne produit pas les lignes dans l’ordre de la clé de tri, alors que le plan situé au-dessus peut déjà s’appuyer sur cet ordre après l’optimisation de lecture ordonnée, ce qui pouvait produire des résultats mal ordonnés. Elle n’est pas non plus appliquée lorsqu’une clauseLIMIT inférieure au nombre de lignes à lire s’applique directement à l’étape de lecture, car la passe obligatoire de construction de l’ensemble rendrait alors l’arrêt anticipé inopérant. #110576 (KochetovNicolai).
  • Le vidage en arrière-plan d’une insertion asynchrone s’arrête désormais rapidement lorsqu’il est interrompu avec KILL QUERY. Auparavant, le vidage d’une charge utile volumineuse mise en tampon poursuivait l’analyse jusqu’au bout sans tenir compte de l’annulation. #110652 (tiandiwonder).
  • Correction de résultats erronés renvoyés par GROUP BY ... optimize_aggregation_in_order et DISTINCT ... optimize_distinct_in_order placés au-dessus d’une jointure partial_merge. L’optimisation de lecture ordonnée était propagée à travers PartialMergeJoin, qui retrie son entrée gauche selon la clé de jointure et ne préserve donc pas l’ordre d’origine du flux gauche, si bien que les lignes étaient mal regroupées. #110671 (groeneai).
  • Correction d’un cas où, lors de l’utilisation d’alias de colonnes de CTE avec des requêtes INTERSECT ou EXCEPT, par exemple WITH t (a, b) AS (SELECT 1, 2 INTERSECT SELECT 1, 2) SELECT a, b FROM t, la requête échouait avec une erreur UNKNOWN_IDENTIFIER. #110885 (yariks5s).
  • Correction d’une erreur LOGICAL_ERROR: ReadBuffer is canceled. Can't read from it. qui pouvait survenir dans une jointure grace_hash lorsque la lecture de blocs déversés sur disque échouait en cours de route alors que plusieurs threads partageaient le lecteur. #111056 (groeneai).
  • Corrige un bug où la matérialisation des colonnes du côté droit lors des *_hash JOINs utilisait le mauvais fuseau horaire. #111074 (yariks5s).
  • Corrige les instructions CREATE comportant une clause COMMENT afin qu’elles lèvent une erreur de syntaxe lorsque la chaîne littérale du commentaire est absente, au lieu d’accepter silencieusement une requête mal analysée (par exemple, CREATE VIEW v COMMENT AS SELECT 1 ne crée plus de vue sans commentaire). #111159 (tiandiwonder).
  • Corrige une erreur LOGICAL_ERROR (Reading from materialized CTE '...' before its materialization completed - DelayedPortsProcessor gate is missing in the query plan) survenant lorsqu’une CTE matérialisée réutilisée (enable_materialized_cte) est lue sur une branche dont la sortie WITH TOTALS ou extremes est ignorée, notamment en cas d’agrégation externe, de INSERT ... SELECT, d’opérations ensemblistes et de EXPLAIN ANALYZE. #111194 (groeneai).
  • Corrige join_algorithm = 'full_sorting_merge', qui remplissait les lignes non jointes d’un LEFT/RIGHT/FULL/ASOFJOIN avec un zéro brut au lieu de la valeur par défaut du type de colonne. Pour une colonne Enum (dont la valeur par défaut est son premier élément, et non 0), cela produisait des résultats incorrects (un filtre sur la valeur de remplissage éliminait les lignes sans correspondance, si bien qu’une jointure externe se comportait silencieusement comme une jointure interne) ainsi qu’une erreur UNKNOWN_ELEMENT_OF_ENUM lorsque la colonne était sélectionnée. #111197 (groeneai).
  • Corrige des résultats incorrects de SummingMergeTree (et CoalescingMergeTree) avec FINAL lorsque optimize_move_to_prewhere_if_final = 1 et que la clause WHERE est une condition booléenne implicite sur une colonne de la clé de tri (par exemple WHERE k). La colonne clé était renvoyée sommée sur les parties de données non fusionnées au lieu d’être conservée telle quelle. #111342 (groeneai).
  • Corrige une erreur logique (Unexpected number of columns in result sample block) pouvant survenir pour un RIGHT/FULL JOIN lorsque le côté gauche contient une colonne dupliquée portant le même nom (par exemple 1 AS b, 1 AS b) qui est conservée à travers un UNION ALL. #111554 (groeneai).
  • Corrige un plantage du client lors du formatage d’une requête EXECUTE AS dont l’AST a un enfant remplacé (comme le fait le fuzzer AST). ASTExecuteAsQuery possède désormais ses enfants target_user et subquery au lieu de conserver des pointeurs non propriétaires vers children. #111725 (groeneai).
  • Corrige des résultats incorrects lorsque query_plan_join_swap_table permute un ANY JOIN alors que join_any_take_last_row = 1 : ClickHouse conserve désormais l’ordre d’origine des côtés de la jointure afin que ANY JOIN renvoie toujours la dernière ligne correspondante. #112458 (vdimir).
  • Corrige l’exception Context has expired lorsque getClientHTTPHeader est utilisé dans une sous-requête. #112533 (alexey-milovidov).
  • Corrige des résultats incorrects pour un JOIN dont la clause ON contient une condition mixte, c’est-à-dire un résidu de non-équijointure portant sur les deux côtés, tel que ON (t1.key = t2.key) AND (t1.a * 10 < t2.a). Seule la famille d’algorithmes de hachage évalue ce type de condition, mais full_sorting_merge, partial_merge et la jointure directe clé-valeur se déclaraient capables d’exécuter ces jointures, puis ignoraient silencieusement le résidu et renvoyaient des lignes en trop. Ils refusent désormais ces jointures, et un algorithme de hachage est alors utilisé à la place. #112831 (groeneai).
  • Correction de résultats erronés pour une jointure par virgule ou CROSS dont la clause WHERE compare des colonnes de deux tables au moyen d’une expression non déterministe telle que rand. Une telle requête pouvait renvoyer des lignes contredisant sa propre clause WHERE, car la réécriture de la jointure en jointure INNER déplaçait l’expression dans la clé de jointure, où elle est réévaluée pour chaque ligne de chaque côté. #112938 (groeneai).
  • Correction d’un ralentissement quadratique lors de l’analyse d’une requête PromQL se terminant par une longue suite de caractères non reconnus (par exemple une valeur FixedString complétée par des octets NUL) : l’analyse d’une entrée invalide de l’ordre du mégaoctet pouvait occuper un thread pendant plusieurs dizaines de minutes sans pouvoir être annulée, car elle a lieu pendant l’analyse de requêtes. Désormais, l’analyseur syntaxique s’arrête à la première erreur. #112944 (alexey-milovidov).
  • Correction de intDiv appliqué à une colonne non constante avec le diviseur constant -1, qui faisait déborder silencieusement la division du plus petit nombre signé (par exemple intDiv(-9223372036854775808, -1)) au lieu de lever l’exception ILLEGAL_DIVISION comme le font les autres chemins d’exécution. Ce débordement silencieux pouvait également produire des résultats ORDER BY/DISTINCT erronés, car l’optimisation de lecture dans l’ordre considère à juste titre intDiv par une constante négative comme monotone, et la valeur débordée enfreignait l’ordre de tri qui en découle. #113048 (alexey-milovidov).
  • Correction de l’erreur UNKNOWN_IDENTIFIER avec enable_analyzer = 0 lorsqu’une clause WHERE fait référence à une colonne produite par untuple dans une sous-requête, par exemple SELECT t.keys FROM (SELECT untuple(arrayJoin(m)) AS t FROM ...) WHERE t.values > 0. La réécriture enable_optimize_predicate_expression poussait un tel prédicat dans la sous-requête sous forme de HAVING, où le nom n’existe pas encore. #113760 (groeneai).
  • Correction de résultats erronés dans les JOIN avec join_algorithm = 'partial_merge' lorsque le côté droit contient des totaux : le côté droit pouvait rester non fusionné, de sorte que les jointures LEFT substituaient des valeurs par défaut aux correspondances réelles et que les jointures INNER/RIGHT ne renvoyaient aucune ligne. Dans le chemin des lignes non jointes de RIGHT/FULL, ce même état provoquait le déréférencement d’un pointeur nul, entraînant le plantage du serveur. #113936 (groeneai).
  • Correction de résultats erronés pour SELECT DISTINCT avec une clause LIMIT devant lire l’intégralité de son entrée : LIMIT n WITH TIES supprimait les lignes ex æquo, exact_rows_before_limit = 1 renvoyait une valeur rows_before_limit_at_least tronquée, et GROUP BY ... WITH TOTALS calculait la ligne des totaux à partir d’un préfixe du flux. #114025 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (« Unexpected number of kept output positions after removing unused columns from ReadFromMergeTree ») levée par une requête SELECT ... FINAL dont la clause PREWHERE utilise une colonne nécessaire à FINAL pour la fusion (ver, is_deleted, sign) sans sélectionner cette colonne. #114746 (groeneai).
  • Correction d’un problème de durée de vie dans le vidage des INSERT asynchrones, le traitement des vues matérialisées et EXPLAIN ANALYZE, qui pouvait entraîner l’accès à un état de suivi de requête expiré. #114940 (azat).
  • Correction de CREATE QUOTA et ALTER QUOTA, qui ignoraient silencieusement tous les blocs FOR INTERVAL sauf le dernier lorsque la virgule séparant les intervalles était omise. Un tel statement s’exécutait sans erreur, mais sans appliquer aucune des limites précédentes. Les intervalles se cumulent de nouveau, comme avant 2020. #115320 (groeneai).
  • Correction de randConstant, qui renvoyait deux valeurs différentes dans une même requête lorsque ses appels portaient des alias différents. Selon la documentation, randConstant conserve une valeur unique pour l’ensemble de la requête, et SELECT randConstant() = randConstant() renvoyait déjà 1, mais l’ajout d’alias, comme dans SELECT randConstant() AS a, randConstant() AS b, produisait deux valeurs sans aucun rapport. #115384 (alexey-milovidov).
  • Correction de SELECT count(), qui ne se terminait jamais ou renvoyait un nombre de lignes fictif sur un fichier MsgPack ou ProtobufList corrompu. Le chemin rapide optimize_count_from_files comptait les lignes sans exiger que la position de lecture avance, si bien qu’un fichier ProtobufList de deux octets produisait des milliards de lignes et ne se terminait jamais. Une telle entrée est désormais rejetée avec la même erreur que celle renvoyée par le chemin de lecture ordinaire. #115884 (groeneai).
  • Correction de broken_data_files dans system.distribution_queue et de la métrique BrokenDistributedFilesToInsert, qui indiquaient toujours 0 : lors du parcours de la file d’attente d’insertion asynchrone au démarrage, DistributedAsyncInsertDirectoryQueue::initializeFilesFromDisk comptabilisait les octets endommagés, mais n’incrémentait jamais le compteur de fichiers endommagés. #92124 (squalfof).

Autres corrections de bogues

  • Les requêtes secondaires exécutées dans le cadre de requêtes internes sont désormais journalisées comme des requêtes internes. #108506 (mstetsyuk).
  • Correction d’un problème où une colonne DEFAULT absente d’une partie de données était remplie avec la valeur par défaut du type au lieu de l’expression DEFAULT lorsque la condition PREWHERE était scindée en plusieurs étapes de lecture. #111795 (yariks5s).
  • Les quotas indexés par normalized_query_hash comptabilisent désormais toutes les ressources (read_rows, read_bytes, result_rows, result_bytes, execution_time, written_bytes, errors) par modèle de requête, à l’instar des compteurs de nombre de requêtes, au lieu de les imputer à un compartiment unique partagé par utilisateur. #107681 (alexey-milovidov).
  • Correction de la non-prise en compte de connect_timeout et des délais de lecture et d’écriture de MySQL : une tentative de connexion à un serveur MySQL qui ne répond pas pouvait rester bloquée plus de deux minutes, même avec connect_timeout = 1, car les signaux périodiques du Sampling query profiler réinitialisaient sans cesse l’échéance interne de poll du client MySQL. #109592 (tiandiwonder).
  • Correction d’un problème où le délai d’attente de bloc des insertions en streaming (input_format_max_block_wait_ms) pouvait ne jamais expirer lorsque le QueryProfiler était actif : le poll du descripteur de fichier repartait avec le délai complet après chaque signal du profiler, si bien qu’une source d’entrée bloquée pouvait retarder indéfiniment le vidage du bloc partiel au lieu de le déclencher une fois la limite d’attente atteinte. #109602 (tiandiwonder).
  • Correction d’une erreur LOGICAL_ERROR (std::length_error) lorsqu’une requête utilisant le paramètre expérimental make_distributed_plan = 1 spécifiait une valeur très élevée pour distributed_plan_default_reader_bucket_count ou distributed_plan_default_shuffle_join_bucket_count. De telles valeurs sont désormais rejetées avec INVALID_SETTING_VALUE. #109770 (groeneai).
  • Correction de distinct_overflow_mode = 'break' : DISTINCT renvoie désormais le résultat partiel accumulé jusqu’à la limite et arrête la lecture de la source, conformément à la documentation. Auparavant, le fragment qui dépassait max_rows_in_distinct / max_bytes_in_distinct était ignoré (résultats tronqués ou vides) et la requête continuait de lire et d’insérer dans le set de hachage jusqu’à la fin de l’entrée, ce qui pouvait aboutir à MEMORY_LIMIT_EXCEEDED. #110075 (skuznetsov-clickhouse).
  • Correction de optimize_aggregation_in_order, qui ignorait l’annulation des requêtes. AggregatingInOrderTransform vérifie désormais l’annulation pendant l’agrégation d’un fragment, de sorte qu’une requête arrêtée par KILL QUERY ou par max_execution_time (avec le mode par défaut timeout_overflow_mode = 'throw') s’interrompt rapidement au lieu de traiter le fragment entier jusqu’au bout. #110104 (groeneai).
  • Correction d’un arrêt brutal du serveur (dans les builds de débogage et avec sanitizer) lorsqu’une valeur provenant d’une source PostgreSQL de la famille mysql/postgresql/sqlite est lue dans un type déclaré vers lequel elle ne peut pas être convertie (par exemple une colonne text déclarée comme Int32). Une telle incompatibilité de type est désormais signalée comme une erreur de requête au lieu de provoquer l’arrêt du serveur. #110264 (groeneai).
  • Correction d’un arrêt brutal du serveur lors de l’analyse de certaines requêtes PRQL (SET dialect = 'prql'). Une panique dans le compilateur prqlc interrompait le processus au lieu d’être signalée comme une erreur de requête. #110316 (groeneai).
  • Correction de NOT_FOUND_COLUMN_IN_BLOCK lorsqu’une politique de ligne et additional_table_filters font référence à la même colonne, non sélectionnée par ailleurs dans la requête. #111099 (groeneai).
  • Correction d’un blocage dans randChiSquared, randStudentT, randFisherF et randBinomial avec des paramètres extrêmes. La requête ne pouvait être interrompue ni par max_execution_time ni par KILL QUERY. De tels paramètres sont désormais rejetés, même lorsque max_rand_distribution_parameter ou max_rand_distribution_trials vaut 0, et max_rand_distribution_trials ne peut plus dépasser sa valeur par défaut de 10^9 pour randBinomial. #111909 (alexey-milovidov).
  • Correction de connection_pool_max_wait_ms afin que sa valeur par défaut documentée, 0, corresponde à un délai d’attente infini. Une requête qui trouvait le pool de connexions plein effectuait auparavant de nouvelles tentatives en boucle serrée, consommant du CPU et journalisant No free connections in pool. Waiting 0 ms. à chaque itération. Elle attend désormais qu’une connexion soit restituée au pool. #112380 (groeneai).
  • Correction de replaceOne, replaceAll, replaceRegexpOne et replaceRegexpAll, qui ignoraient max_execution_time et KILL QUERY. Un appel unique vérifie désormais l’annulation en cours d’exécution, de sorte qu’un remplacement de longue durée sur une valeur volumineuse ou sur un grand nombre de lignes s’arrête au lieu d’occuper un thread du serveur jusqu’à son terme. Un tel appel n’ayant aucun résultat partiel à renvoyer, il lève le dépassement de l’échéance sous forme d’erreur lorsque timeout_overflow_mode = 'break' : systématiquement lorsqu’il est évalué par repliement de constantes, et dans le pipeline chaque fois que ce point de contrôle est atteint avant la vérification entre blocs de l’executor, qui continue d’arrêter la requête silencieusement. #112483 (groeneai).
  • Correction de countSubstrings, countSubstringsCaseInsensitive et countSubstringsCaseInsensitiveUTF8, qui ignoraient KILL QUERY et max_execution_time : un appel portant sur de nombreuses lignes ou de nombreuses correspondances pouvait continuer à s’exécuter pendant plusieurs minutes après l’annulation de la requête. #113369 (groeneai).
  • Correction d’un possible blocage d’une requête (pendant receive_timeout secondes) lorsque celle-ci se termine très rapidement sur l’initiateur. #113551 (nickitat).
  • Correction de countMatches et countMatchesCaseInsensitive, qui ignoraient max_execution_time et KILL QUERY lors du comptage des correspondances dans une valeur volumineuse, ce qui pouvait maintenir une requête annulée en cours d’exécution pendant plusieurs minutes. Avec un argument constant, l’appel a lieu pendant l’analyse de requêtes ; il n’existe donc aucun pipeline permettant de l’annuler. #113886 (groeneai).
  • Une requête pouvait être annulée jusqu’à 1 ms avant l’expiration de son max_execution_time, et échouer avec le message contradictoire Timeout exceeded: elapsed 999.672 ms, maximum: 1000 ms. #114559 (alexey-milovidov).
  • Correction d’une lecture hors limites dans pointInPolygon avec un polygone constant dont la boîte englobante n’est pas bornée, par exemple lorsqu’une coordonnée atteint +-DBL_MAX. Un tel polygone est désormais rejeté avec BAD_ARGUMENTS, et un polygone comportant un anneau vide est considéré comme dépourvu d’intérieur. Auparavant, la requête pouvait renvoyer un résultat erroné, voire faire planter le serveur dans les builds où le durcissement ou les sanitizers étaient activés. #115035 (groeneai).
  • Correction d’une condition de concurrence qui pouvait amener une requête STREAM BOUNDED à renvoyer moins de lignes que celles validées avant son démarrage, le plus souvent aucune. #115543 (alexey-milovidov).
  • Correction de l’erreur Unknown identifier levée lors de l’optimisation du plan de requête avec make_distributed_plan = 1 lorsque la colonne de tri d’une requête de fenêtrage passe par une étape Expression qui ne la mentionne pas. #115890 (groeneai).
  • Correction de h3kRing, h3HexRing, h3Line et h3ToChildren, qui ignoraient max_execution_time et KILL QUERY lors de l’expansion d’un bloc de lignes. #115893 (alexey-milovidov).
  • Correction de l’erreur NOT_FOUND_COLUMN_IN_BLOCK dans le lecteur Parquet V3 natif (à la suite d’une régression antérieure) lorsqu’un PREWHERE comportait des conjonctions partageant une expression intermédiaire commune. #107059 (groeneai).
  • Correction d’un blocage lors de l’écriture de fichiers Parquet avec output_format_parquet_parallel_encoding activé (valeur par défaut) et max_threads supérieur à 1. Si l’encodeur ne parvenait pas à planifier un thread supplémentaire, son compteur de threads actifs subissait un dépassement par le bas (underflow) et l’écriture pouvait se bloquer définitivement au lieu de se terminer ou de signaler une erreur. #112959 (groeneai).
  • Correction d’une allocation mémoire non bornée lors de la lecture d’un fichier Parquet dont les métadonnées Thrift déclarent une longueur de conteneur ou de chaîne supérieure à la taille des métadonnées elles-mêmes. Une telle longueur est désormais rejetée au lieu de donner lieu à une allocation. #113212 (groeneai).
  • Correction d’une exception std::out_of_range (Logical error: 'std::exception. Code: 1001, type: std::out_of_range', qui provoque également l’arrêt du serveur dans les builds de débogage et avec sanitizer) dans le lecteur Parquet v3 lorsqu’une colonne nécessaire uniquement à l’évaluation d’un filtre était planifiée pour décodage alors que son emplacement de sortie avait déjà été supprimé. #113956 (groeneai).
  • Correction de data races dans les profile events et le memory tracker. #112466 (mstetsyuk).
  • Correction d’une data race théorique dans les profile events lors de l’utilisation du paramètre trace_profile_events_list pour écrire les stack traces de certains profile events dans system.trace_log. #113553 (mstetsyuk).
  • Correction de intDivOrNull, moduloOrNull et positiveModuloOrNull, qui renvoyaient 0 au lieu de NULL, ainsi que de intDivOrNull et intDivOrZero, qui levaient une exception au lieu de renvoyer NULL / 0, lorsque la division entraîne une exception en virgule flottante (division par zéro ou INT_MIN / -1), y compris avec des arguments mixtes signés/non signés. #101976 (yariks5s).
  • Correction de IS DISTINCT FROM pour les valeurs Array et Map comparées à NULL. #103162 (ylw510).
  • Correction de groupConcat lorsque les syntaxes paramétrique et à deux arguments sont combinées, par exemple groupConcat(',', 2)(x, '/') : le paramètre de limite de lignes était ignoré silencieusement, si bien que toutes les lignes étaient renvoyées au lieu du nombre demandé ; le délimiteur fourni par le second argument remplace désormais correctement le paramètre. #104882 (yariks5s).
  • Correction de bogues lors de l’utilisation de CAST d’unités d’intervalle plus petites vers des unités plus grandes. #105058 (yariks5s).
  • Correction d’une erreur logique (Assertion 'row < chunk.getNumRows()' failed) dans les requêtes LIMIT ... WITH TIES exécutées avec le pipeline de lecture ordonnée (optimize_read_in_order = 1 et read_in_order_use_virtual_row_per_block = 1). #105102 (groeneai).
  • Correction de Logical error: 'Pipeline stuck' dans les requêtes qui utilisent enable_sharding_aggregator = 1 conjointement avec un UNION ALL et une valeur de max_streams_for_union_step inférieure à la largeur du pipeline. BufferedShardByHashTransform termine désormais les ports de sortie dont la file d’attente est vide dès que son entrée est épuisée, et continue de lire l’entrée lorsqu’un port vide sollicité n’a rien à vider, même si une file d’attente voisine a atteint la limite de contre-pression. #106251 (groeneai).
  • Correction de removeDirectory sur les disques de stockage objet plain (tels que s3_plain), qui supprimait tous les fichiers d’un répertoire non vide comme si la suppression était récursive. La suppression d’un répertoire non vide échoue désormais avec CANNOT_RMDIR, et la suppression récursive traite explicitement le contenu. #106281 (RinChanNOWWW).
  • Correction des fonctions définies par l’utilisateur executable_pool configurées avec <lifetime>, qui ne prenaient pas en compte les modifications du script sous-jacent. Auparavant, seul SYSTEM RELOAD FUNCTIONS relisait un script modifié ; les rechargements périodiques via <lifetime> continuaient d’exécuter l’ancienne version. #107087 (groeneai).
  • Correction d’une condition de concurrence sur le collecteur de traces global du serveur, entre le démarrage du profileur d’un thread de travail et l’arrêt du serveur. #107307 (groeneai).
  • Correction d’un rare plantage du serveur lors de son arrêt (std::future_error, « The associated promise has been destructed prior to the associated state becoming ready ») provoqué par une tâche planifiée via threadPoolCallbackRunnerUnsafe et retirée de la file d’attente du thread pool avant son exécution. #107383 (groeneai).
  • Correction d’une erreur logique de FileLog (Last stored last_written_position in meta file ... is bigger than current last_written_pos) qui pouvait survenir lorsqu’un fichier surveillé était supprimé puis recréé avec le même inode. #107617 (groeneai).
  • Correction de toTime64 et CAST(... AS Time64), qui ne ramenaient pas les valeurs hors plage dans la plage de Time64 avec les modes de dépassement de capacité saturate et ignore, ce qui pouvait produire des valeurs affichées à l’identique mais considérées comme différentes lors des comparaisons. #108028 (alexey-milovidov).
  • Correction de la gestion des assertions de largeur nulle (\b, ^, $) dans les fonctions regexp de type « match all » extractAll, extractAllGroupsVertical, extractAllGroupsHorizontal, countMatches et splitByRegexp. Par exemple, extractAll('new york is the greatest', '\b(\w)') renvoie désormais correctement la première lettre de chaque mot au lieu de chaque lettre. #108047 (alexey-milovidov).
  • Correction de résultats incorrects lors de l’utilisation de hasToken sur des index de texte avec un tokenizer autre que splitByNonAlpha. #108066 (rschu1ze).
  • Correction de toStartOfInterval et dateTrunc, qui renvoyaient une valeur arrondie au lieu du début de l’intervalle englobant lorsque l’entrée avait une précision plus fine que l’unité de l’intervalle ; par exemple, toStartOfInterval(toDateTime64('2023-10-09 10:11:12.000999', 6), INTERVAL 1 millisecond) renvoyait 10:11:12.001 au lieu de 10:11:12.000. La surcharge avec une origine explicite était également concernée. #108186 (yariks5s).
  • Correction de replaceRegexpOne et replaceRegexpAll afin que . corresponde par défaut aux caractères de nouvelle ligne, à l’instar des autres fonctions d’expressions régulières. #108265 (linjiayu1025-collab).
  • Correction de la double évaluation des conditions des UPDATE/DELETE légers. #108323 (ofeliacode).
  • Correction de arrayPartialSort() avec un argument de limite non constant. #108327 (vitlibar).
  • Correction des fonctions d’expressions régulières (match, extract, extractAll, replaceRegexpOne, replaceRegexpAll, etc.) qui renvoyaient silencieusement des résultats erronés lorsque le motif contenait un octet NUL (\0). L’octet NUL est désormais traité comme un octet littéral ordinaire, conformément au comportement de RE2. #108427 (alexey-milovidov).
  • Correction de changeYear/changeMonth/changeDay/changeHour/changeMinute/changeSecond appliquées à un argument DateTime64(N), qui créaient la colonne de résultat avec la scale par défaut codée en dur (3) au lieu de N. Les valeurs étaient calculées avec la scale N, mais l’objet colonne déclarait une scale de 3, ce qui le rendait structurellement incohérent avec son type. #108551 (groeneai).
  • Correction d’une erreur LOGICAL_ERROR (« Trying to extract chunk from ChunkBuffer before all inputs are finished ») lorsqu’une opération ensembliste (INTERSECT / UNION ALL / EXCEPT) combine des branches utilisant des sous-requêtes corrélées avec correlated_subqueries_default_join_kind = 'left'. #108554 (groeneai).
  • Correction de bogues liés à DateTime64 dans changeYear, changeMonth, changeDay, changeHour, changeMinute et changeSecond : les entrées avec une precision à la nanoseconde (scale 9) ne lèvent plus DECIMAL_OVERFLOW, et les entrées inférieures à la seconde antérieures à l’epoch renvoient désormais la seconde calendaire correcte. #108681 (takumihara).
  • Suppression des sorties excessives dans le journal du serveur et des messages d’erreur démesurés lors de la compilation d’une très grande expression régulière (par exemple un motif LIKE ou match comportant des centaines de milliers de jokers) ; ces motifs échouent désormais avec une erreur explicite CANNOT_COMPILE_REGEXP. #108821 (Algunenano).
  • Correction de reinterpret(x, 'Decimal128(scale)') (ainsi que des cibles Decimal32/Decimal64/Decimal256/DateTime64), qui produisait une colonne de résultat dont la scale interne était celle de la source au lieu de la scale cible demandée lorsque la source et la cible avaient le même type physique. Les valeurs étaient correctes, mais l’objet colonne était structurellement incohérent avec son type déclaré. #108878 (groeneai).
  • Correction d’un comportement indéfini lors de la conversion en chaîne d’un type de paquet de protocole hors limites (par exemple dans les messages d’erreur « Unexpected packet from server » / « Received … packet ») pour une connexion désynchronisée ou soumise à du fuzzing. #108885 (groeneai).
  • Correction de changeYear/changeMonth/changeDay/changeHour/changeMinute/changeSecond sur DateTime64(N), qui construisaient la colonne de résultat avec la scale par défaut codée en dur à 3 au lieu de N, ainsi que de timeSlots sur DateTime64, qui ignorait la scale de l’argument facultatif Size dans son type de retour déclaré. Dans les deux cas, la colonne produite avait une scale physique différente de son type déclaré, ce qui provoquait une LOGICAL_ERROR (writeSlice expects same column types) lorsque le résultat était passé à arrayPushBack/arrayPushFront/arrayConcat, et timeSlots renvoyait des timestamps erronés lorsque Size avait la plus grande scale. #108994 (groeneai).
  • Correction des incréments non monotones des ProfileEvents #109180 (azat).
  • Correction des ratios SAMPLE dont l’exposant est trop grand en valeur absolue pour tenir dans un Int32 (par ex. SAMPLE 1e-3000000000), qui étaient traités silencieusement comme SAMPLE 1. #109197 (Algunenano).
  • Correction de deux bogues dans le traitement DateTime64 de toUTCTimestamp/fromUTCTimestamp (et de leurs alias to_utc_timestamp/from_utc_timestamp) ainsi que de toTime64. D’une part, un dépassement de capacité d’entier signé près de la limite de Int64 (le calcul est désormais effectué en Int128 et borné à la plage représentable). D’autre part, un résultat erroné pour les valeurs fractionnaires négatives proches d’un changement de décalage de fuseau horaire : la séparation des secondes tronquait vers zéro, si bien que timezoneOffset() examinait la seconde suivante et pouvait se placer du mauvais côté d’un changement d’heure (DST) ou de décalage. #109738 (groeneai).
  • Correction d’une erreur logique (column->size() == num_rows) lorsqu’une expression de filtre de politique de ligne utilise arrayJoin ; ces filtres sont désormais rejetés. #109753 (Algunenano).
  • Correction de generateRandomStructure(), qui produisait parfois une chaîne de structure invalide contenant deux types de données concaténés (par exemple Decimal32(7)IPv4) lorsque l’imbrication des types dépassait la limite de profondeur interne, ce qui rendait le résultat impossible à analyser. #109928 (groeneai).
  • Correction de la propagation des paramètres de requête dans accurateCastOrDefault et de la préservation des NULL d’origine encodés par Dynamic et Variant. #109946 (Avogar). #114912 (alexey-milovidov).
  • Correction de max_bytes_in_distinct et max_bytes_in_set : les clés de type string sont stockées dans une arena qui n’était pas prise en compte par les vérifications de limite, si bien que DISTINCT / IN sur des clés string pouvaient occuper une quantité de mémoire dépassant la limite en octets de toute la taille des clés (sans borne, quelle que soit la longueur des clés). Les limites tiennent désormais compte de l’arena, conformément à leur documentation ; les requêtes avec des clés string proches d’une limite en octets peuvent donc l’atteindre plus tôt (à juste titre). #110120 (skuznetsov-clickhouse).
  • Correction de la lecture des fichiers Npy comportant des dimensions internes de taille nulle, afin que les lignes matérialisées et les résultats de count() optimisés et non optimisés concordent. #110146 (qiuyanjun888).
  • Correction d’une erreur logique (arrêt du serveur dans les constructions debug/sanitizer) lorsque les fonctions indexHint/ignore/isZeroOrNull reçoivent un argument dont le type se résout en Nothing, par exemple dans des expressions comme indexHint(assumeNotNull(materialize(NULL))). #110192 (groeneai).
  • Correction de la lecture des fichiers sur les disques plain_rewritable après la réécriture d’un path existant avec un contenu de taille différente : les métadonnées en mémoire conservaient l’ancienne taille du fichier, ce qui pouvait faire échouer les lectures avec UNEXPECTED_END_OF_FILE. #110304 (thevar1able).
  • Rejet des valeurs de vector_search_index_fetch_multiplier inférieures à 1.0 afin d’éviter des résultats de recherche ANN vides lorsque le nombre de récupérations calculé est tronqué à zéro. #110452 (tamish560).
  • Correction d’une erreur sporadique Cannot parse string ... as UInt64 ... While executing ValuesBlockInputFormat pouvant survenir lors de l’insertion de valeurs dont les expressions contiennent une conversion numérique telle que initializeAggregation('sumState', 0::UInt64). #110637 (groeneai).
  • Correction de readWKT, qui rejetait les strings WKT commençant par des espaces (par ex. readWKT(' POINT(1 2)')), pourtant acceptées par les lecteurs typés readWKTPoint/readWKTPolygon/… ainsi que par la grammaire WKT. #110706 (groeneai).
  • Correction d’un plantage du serveur (division entière par zéro) lors de la consommation d’un quota dont l’intervalle est de durée nulle (par exemple CREATE QUOTA q FOR INTERVAL 0 SECOND MAX queries = 1000). Une durée d’intervalle de quota négative ou nulle est désormais rejetée dès la création du quota. #110846 (PedroTadim).
  • Correction d’un plantage dans les constructions FIPS lors de l’utilisation d’une clé SSH Ed25519 (CREATE USER ... IDENTIFIED WITH ssh_key ... TYPE 'ssh-ed25519', ou une telle clé dans users.xml). Ed25519 n’étant pas approuvé par FIPS, ces clés sont désormais rejetées avec une erreur LIBSSH_ERROR explicite. #110891 (thevar1able).
  • Les fonctions de distance transposées sur QBit (cosineDistanceTransposed, L2DistanceTransposed, dotProductTransposed) utilisent désormais une reconstruction bornée à faible précision d’une valeur tronquée à precision plans de bits, au lieu de la reconstruire à la borne inférieure de la cellule grossière (bits supprimés remplis de zéros). Le remplissage par des zéros biaisait chaque valeur reconstruite vers zéro et dégénérait à faible précision : pour QBit(BFloat16) avec une précision de 1, seul le bit de signe subsistait, si bien que toutes les valeurs étaient reconstruites à ±0.0 et que la distance était une même constante pour toutes les lignes, sans aucune information de classement. La reconstruction dépend désormais des bits supprimés : les codes Int8 bruts sont reconstruits au centre de leur cellule (comme la reconstruction Lloyd-Max déjà utilisée par le chemin ...TransposedQuantized) ; une valeur à virgule flottante avec une précision de 1 devient une véritable quantification de signe ; une valeur à virgule flottante qui conserve l’intégralité de son exposant et ne perd que des bits de mantisse est reconstruite au point médian borné de sa propre binade (tandis qu’un véritable zéro ou ±0.0 reste exactement à zéro et que la cellule non finie est isolée afin que ±inf reste exactement infini ; voir la remarque ci-dessous sur la cellule ambiguë NaN) ; enfin, tant que des bits d’exposant sont encore tronqués, la borne inférieure bornée est conservée, de sorte qu’une precision plus faible sacrifie de l’exactitude au profit de la vitesse, sans gonfler une valeur de plusieurs ordres de grandeur. Les résultats en pleine précision restent inchangés. #110911 (alexey-milovidov).
  • Correction d’une lecture hors limites dans le lecteur ORC natif lors de l’inférence de schéma d’un fichier ORC corrompu dont l’arbre de types déclare plus de colonnes que le pied de stripe ne contient d’encodages de colonnes. Ce fichier est désormais rejeté avec CANNOT_EXTRACT_TABLE_STRUCTURE au lieu de provoquer l’arrêt du serveur dans les constructions debug/sanitizer. #110967 (groeneai).
  • Chaque clé SSH compte désormais individuellement dans la limite max_authentication_methods_per_user, afin que la limite s’applique de manière cohérente quelle que soit la syntaxe. Auparavant, plusieurs clés déclarées dans une même méthode ssh_key ne comptaient que pour une seule. #111181 (TheMC47).
  • Les catalogues REST Iceberg signalent désormais les identifiants expirés en indiquant l’erreur sous-jacente, au lieu de renvoyer silencieusement UNKNOWN_TABLE. #111379 (alesapin).
  • Correction de readWKTPoint/readWKT, qui renvoyaient des coordonnées non initialisées (en mode scalaire) ou celles de la ligne précédente (en mode vectorisé) pour un point vide avec indicateur de dimension tel que POINT M EMPTY. Ces entrées sont désormais rejetées avec CANNOT_PARSE_TEXT, comme c’est déjà le cas pour POINT EMPTY. #111517 (groeneai).
  • La création de statistiques minmax émet désormais un avertissement au lieu de lever une exception. #111785 (hanfei1991).
  • randBinomial reste utilisable avec les probabilités dégénérées 0 et 1, quel que soit le nombre d’essais. #112021 (alexey-milovidov).
  • Correction de system.s3_queue_settings, qui renvoyait des valeurs incorrectes pour bucketing_mode, partitioning_mode, partition_regex et partition_component. #112300 (bharatnc).
  • Les erreurs dans les requêtes PromQL indiquent désormais la position correcte. #112494 (fallintoplace).
  • SYSTEM DISABLE FAILPOINT rejette désormais un nom de fail point inexistant en levant BAD_ARGUMENTS, comme le faisait déjà SYSTEM ENABLE FAILPOINT. Auparavant, un nom mal saisi renvoyait un succès tout en laissant activé le fail point visé, sans aucune indication que rien n’avait été désactivé. #112680 (groeneai).
  • Correction d’une lecture mal typée d’une sous-colonne d’élément Nested dont la colonne avait été supprimée puis rajoutée. Sélectionner une telle sous-colonne en même temps que sa propre colonne parente renvoyait l’élément entier à l’emplacement de la sous-colonne, alors que le bloc déclarait toujours le type d’élément. Cela produisait des valeurs arbitraires dans les constructions de production, et une erreur logique dans les constructions de débogage et avec sanitizer. #112769 (groeneai).
  • Rejet des durées PromQL dont les unités sont répétées ou ne sont pas classées de la plus longue à la plus courte. #112954 (fallintoplace).
  • Correction d’un comportement indéfini (pointeur nul passé à memcpy) dans replaceRegexpOne/replaceRegexpAll lorsque la string de remplacement fait référence à un groupe capturant qui n’a pas participé à la correspondance, par exemple replaceRegexpAll('abc', '(a)|(b)', '\\2'). #113012 (alexey-milovidov).
  • Correction d’une incompatibilité empêchant le chargement, dans les versions ultérieures, des index de texte créés dans ClickHouse 26.3 avec le tokenizer unicode_word #113061 (rschu1ze).
  • Rejet des points de code Unicode de substitution (surrogates) invalides dans les séquences d’échappement des strings PromQL. #113203 (fallintoplace).
  • Rejet des échappements de délimiteurs croisés invalides dans les strings PromQL. #113587 (fallintoplace).
  • Correction de theilsU sur un frame de fenêtre, qui renvoyait une valeur arbitraire au lieu de 0 lorsque le premier argument est constant au sein du frame. #113691 (alexey-milovidov).
  • Correction du code d’erreur signalé pour certaines colonnes sérialisées Replicated malformées reçues via le protocole Native : elles sont désormais rejetées avec INCORRECT_DATA au lieu de LOGICAL_ERROR (qui provoquait l’arrêt du serveur dans les builds de débogage et les builds avec sanitizer). #114186 (Avogar).
  • Correction des littéraux Time64 et des constantes de liste IN incorrects, souvent de signe inversé, produits lorsque la remise à l’échelle d’un Decimal64 d’échelle inférieure provoquait un dépassement de capacité de Int64. Une telle conversion signale désormais DECIMAL_OVERFLOW, à l’instar de la branche DateTime64 et du CAST explicite. #114546 (groeneai).
  • Prise en charge des noms de métriques et de labels entre guillemets dans les sélecteurs PromQL. #114551 (fallintoplace).
  • Correction des tables ReplicatedMergeTree qui restaient indéfiniment en lecture seule après l’échec d’une tentative de démarrage exécutée via le thread d’attachement. La nouvelle tentative de redémarrage est désormais correctement planifiée, ce qui rétablit la récupération automatique et ses vérifications de session. #114802 (azat).
  • timeSeriesRange et timeSeriesFromGrid levaient à tort une erreur DECIMAL_OVERFLOW pour les timestamps antérieurs à 1970, car les timestamps de début et de fin étaient lus comme des valeurs non signées. En outre, pour les timestamps DateTime/UInt32, le pas était silencieusement tronqué à ses 32 bits de poids faible. Tous les calculs sont désormais effectués en Int64. #114815 (vitlibar).
  • Correction d’une exception susceptible de se produire dans les insertions asynchrones après l’échec d’une requête avec MEMORY_LIMIT_EXCEEDED, et correction de async_insert_queue_flush_on_shutdown afin que les insertions en file d’attente soient écrites sur disque lors de l’arrêt du serveur. #114839 (azat).
  • Correction d’une exception out_of_range levée sous la forme d’une erreur interne Code: 1001 lorsqu’un motif sequenceMatch, sequenceCount ou sequenceMatchEvents contient le numéro d’événement 0, par exemple sequenceMatch('(?0)'). Un tel motif est désormais rejeté avec BAD_ARGUMENTS. Une condition temporelle ne contenant qu’un signe, comme sequenceMatch('(?1)(?t>+)(?2)'), était silencieusement interprétée comme (?t>0) ; elle est désormais rejetée avec SYNTAX_ERROR. #115056 (groeneai).
  • Correction du message d’erreur de geoToH3, qui indiquait une position d’argument et un type erronés lorsqu’une coordonnée n’est pas de type Float64. Avec la valeur par défaut geotoh3_argument_order = 'lat_lon', le message désignait l’autre argument et affichait le type de celui-ci. #115503 (groeneai).
  • Correction des caractères d’échappement antislash invalides dans les index text, tokenbf_v1 et ngrambf_v1. #115634 (ahmadov).
  • Correction d’un problème empêchant le serveur de démarrer lorsque la variable d’environnement TZ était vide. #68921 (ardenwick).
Dernière modification le 14 octobre 2026