Skip to main content
ClickHouse Connect peut décoder les résultats de requête et encoder les insertions à l’aide d’un codec Rust compilé plutôt qu’avec l’implémentation Python et Cython par défaut. Le codec Rust s’applique uniquement au trafic FORMAT Native géré par le client, ce qui couvre query, query_np, query_df, leurs variantes de streaming par bloc et par ligne, ainsi que les insertions, y compris insert_df. Les méthodes Arrow utilisent FORMAT Arrow et ne sont donc pas concernées, pas plus que les requêtes raw, les insertions raw et les formats non natifs. Le codec est expérimental et son utilisation est optionnelle. Le codec Python reste celui utilisé par défaut.

Installation

Le codec compilé est fourni sous la forme d’un paquet wheel distinct, nommé clickhouse-connect-core, qui contient le module d’extension _ch_core. Installez le codec avec les intégrations utilisées par votre application :
Des wheels Core sont publiés pour CPython 3.10 à 3.14 ainsi que pour CPython 3.14t en mode free-threaded, sous Linux, macOS et Windows. Sous Python free-threaded, l’importation de l’extension Core ne réactive pas le GIL. La sortie NumPy et Pandas standard en Rust ne nécessite ni PyArrow ni aucun autre paquet Arrow. Cela concerne query_np, query_df, leurs variantes en streaming, ainsi que query(..., use_numpy=True). Les requêtes NumPy ne nécessitent pas Pandas. Ajoutez l’extra async pour utiliser le client asynchrone. PyArrow reste nécessaire pour les méthodes Arrow et le stockage Pandas basé sur Arrow. Installez l’extra arrow pour bénéficier de ces fonctionnalités :
La sortie étendue des strings Pandas suit l’option mode.string_storage de Pandas. Lorsque PyArrow est installé, les strings utilisent la conversion Arrow existante, avec une validation UTF-8 complète, quel que soit le backend de stockage. Sans PyArrow, le driver utilise la conversion d’objets Rust, et le stockage par défaut de Pandas utilise des strings Python. Demander explicitement le stockage de strings Arrow sans PyArrow lève une erreur de dépendance. Les données UTF-8 invalides conservent le rendu hexadécimal existant, quel que soit le backend de stockage. Le codec, son empaquetage et ses dépendances sont expérimentaux. La conversion directe par tampon prend désormais en charge les colonnes d’integers non nullable de 8 à 64 bits, ainsi que les colonnes Float32/64, Boolean, BFloat16, Interval, Date, Date32, DateTime et DateTime64. Les colonnes scalaires Time et Time64, y compris les valeurs nullable, utilisent également des tampons directs, tout comme BFloat16 nullable et la sortie Pandas étendue des integers, flottants et Interval nullable. BFloat16 conserve une sortie float32 ainsi que sa politique actuelle de gestion des valeurs manquantes Float32 dans Pandas. Les valeurs Interval restent des décomptes signés exprimés dans leur unité déclarée. La conversion temporelle conserve les unités déclarées et les règles de fuseau horaire existantes. Les alias SimpleAggregateFunction nullable de Time64 renvoient désormais des durées correctes, et NaT pour les valeurs NULL. Les colonnes scalaires nullable DateTime64(9) utilisent également des tampons afin de préserver les nanosecondes dans la sortie NumPy et Pandas. Les autres dates et timestamp nullable conservent leurs chemins de conversion d’objets. Les tableaux de Time/Time64, y compris les tableaux imbriqués et les feuilles nullable, ainsi que LowCardinality(Time), y compris les valeurs nullable, utilisent également des tampons directs. Leur imbrication, leurs unités de durée et leur représentation des valeurs nulles restent inchangées. LowCardinality(Time64) n’est toujours pas pris en charge par le cœur Rust. Pour les colonnes scalaires nullable DateTime64(9), query_df et query_df_stream préservent les neuf chiffres fractionnaires, y compris via les alias SimpleAggregateFunction. Sous Pandas 3, les colonnes datetime de la sortie par défaut utilisent désormais la nanoseconde comme unité, et non plus la microseconde. L’inférence existante du dtype object pour les blocs entièrement NULL reste inchangée. Si un fuseau horaire nommé fait sortir une valeur de la plage en nanosecondes de Pandas, la colonne concernée conserve sa conversion d’objets et son comportement de plage antérieurs. Pour ces colonnes, les champs object de query_np et query_np_stream contiennent désormais des cellules numpy.datetime64 au lieu de cellules Python datetime, avec None pour les NULL SQL. Les valeurs associées à un fuseau nommé utilisent des ticks UTC sans métadonnées de fuseau horaire, comme le codec Python. Les nanosecondes sont ainsi préservées sans nécessiter Pandas. Utilisez query_df si vous avez besoin de timestamp tenant compte du fuseau horaire. Les autres types de cellules temporelles NumPy nullable restent inchangés. Les requêtes NumPy et Pandas sur des DateTime64 scalaires rejettent toute précision autre que 0, 3, 6 et 9 avec une ProgrammingError, y compris pour les colonnes nullable et les alias SimpleAggregateFunction. Si un codec Rust est sélectionné alors que le module compilé n’est pas installé, la création du client lève une NotSupportedError indiquant cette commande d’installation.

Activation du codec

Sélectionnez le codec via l’option client native_codec :
Valeurs acceptées : La valeur par défaut peut également être définie via le paramètre commun native_codec ou la variable d’environnement CLICKHOUSE_CONNECT_NATIVE_CODEC. L’ordre de préséance est le suivant : l’argument nommé du client, puis le paramètre commun, puis la variable d’environnement. L’option est ignorée pour les clients interface="chdb", qui utilisent toujours le codec Python.

Quand utiliser le codec Rust

Le codec Rust est particulièrement utile pour les résultats DataFrame volumineux comportant du texte, des conteneurs et des types complexes tels que String, LowCardinality, Map, Array, JSON, Decimal et UUID. Il peut également bénéficier aux flux volumineux de blocs de lignes et de colonnes, aux charges de travail de requêtes concurrentes et aux insertions en masse. Les petits résultats et les requêtes limitées par le réseau ne montreront sans doute que peu de différence. Les résultats numériques plats empruntent déjà des chemins d’accès en masse efficaces dans le codec Python : le gain y sera donc lui aussi plus limité. Les tampons directs évitent de passer par des tableaux Arrow intermédiaires pour les types pris en charge. L’assemblage final des résultats NumPy et Pandas peut toutefois encore nécessiter des allocations et des copies : tous les résultats de requête ne sont donc pas pour autant zero-copy. Sans PyArrow, la conversion des strings par Rust construit des objets string Python à partir de chaque bloc décodé. Avec les tailles de bloc par défaut, le pic de mémoire pour les résultats String volumineux est proche de celui du codec Python. Les très grands blocs, par exemple un bloc unique contenant un million de lignes, maintiennent l’intégralité du bloc décodé en mémoire pendant la construction de ses strings et peuvent entraîner un pic de mémoire supérieur à celui du codec Python. Pour les résultats Pandas StringDtype volumineux, installez l’extra arrow et utilisez le stockage de strings Arrow. Pandas 3 sélectionne le stockage Arrow par défaut lorsque PyArrow est installé. Pandas 2 nécessite pd.options.mode.string_storage = "pyarrow". Les appels query() bufferisés portant sur des résultats très larges ou entièrement numériques peuvent actuellement s’avérer plus lents et consommer davantage de mémoire de pointe avec le codec Rust. Pour ces charges de travail, privilégiez query_df, query_row_block_stream ou query_column_block_stream. Pour NumPy et Pandas, query_np_stream et query_df_stream permettent également de réduire le pic de mémoire. Traitez et libérez chaque bloc au fur et à mesure plutôt que de collecter l’intégralité du flux. La consommation de mémoire reste fonction de la taille de chaque bloc : diffuser en streaming un seul bloc volumineux n’apporte donc pas le même avantage. Évaluez les performances sur votre propre charge de travail avant d’adopter le codec. Utilisez native_codec="rust_strict" pendant vos mesures afin qu’une option non prise en charge ou une dépendance manquante lève une erreur au lieu de rediriger silencieusement la requête vers Python.

Règles de fallback

Les décisions de fallback sont prises avant que le moindre octet ne soit lu ou envoyé : aucun changement de codec ne survient donc en cours de stream. Pour les requêtes, le choix intervient avant la consommation du response body. Pour les insertions, l’encodeur Rust n’est retenu que si tous les types de colonnes sont pris en charge ; sinon, l’insertion s’exécute intégralement avec le codec Python. Lorsque naive_datetime_insert="server" est actif, rust route vers le codec Python toute insertion contenant une colonne DateTime ou DateTime64, afin que la timezone déclarée de la colonne ou la server timezone soit appliquée. rust_strict rejette cette combinaison. Le mode par défaut naive_datetime_insert="local" continue d’utiliser l’encodeur Rust. Les requêtes de metadata internes au driver, y compris les statements de reflection du dialect SQLAlchemy, passent toujours par le codec Python, de façon silencieuse, quel que soit le mode. Les payloads Native malformés détectés par le codec Rust lèvent DataError.

Versioning

clickhouse-connect-core est versionné indépendamment de clickhouse-connect. L’extra rust requiert clickhouse-connect-core>=0.2.1,<0.3. Le driver vérifie à la fois la version de l’API de binding et la capability column-buffer lorsqu’un codec Rust est sélectionné. Le wheel core 0.2.0 publié ne dispose pas de la capability column-buffer. Si le wheel installé est incompatible, la création du client lève une NotSupportedError pour rust comme pour rust_strict, indiquant la commande d’upgrade suivante :
Les corrections et les améliorations de performances du codec compilé sont livrées dans les releases de clickhouse-connect-core et peuvent être obtenues par une simple mise à niveau vers un wheel core compatible. Les modifications de l’intégration Rust du driver nécessitent en revanche une mise à niveau de clickhouse-connect.

Différences de comportement connues

Le codec Rust vise une parité cellule par cellule avec le codec Python. Les différences suivantes sont connues.
  • Les résultats de query_np et query_df pour les colonnes Variant contiennent des objets Python simples plutôt que des valeurs scalaires numpy. Les valeurs sont identiques, mais les types de cellules diffèrent.
  • Les valeurs Dynamic contenant Time64 sont matérialisées sous la forme datetime.timedelta dans les résultats Rust de query_np et query_df. Aux échelles 0, 3, 6 et 9, les valeurs sont égales aux cellules numpy.timedelta64 du codec Python, mais les types de cellules diffèrent. Aux autres échelles, le codec Rust renvoie datetime.timedelta alors que le codec Python lève ProgrammingError, car NumPy n’a pas d’unité correspondante. Les métadonnées des membres Dynamic ne sont pas exposées au driver après le décodage Rust : utilisez donc native_codec="python" lorsque les types de cellules NumPy ou la validation des échelles non prises en charge sont nécessaires.
  • Pour query_df, le codec Python peut convertir en chaîne les valeurs Compound stockées dans les shared data JSON. Le codec Rust renvoie des objets décodés, ce qui correspond aux résultats de query_np des deux codecs.
  • Une alternative LowCardinality placée dans un conteneur qui se matérialise cellule par cellule, comme Array(Variant(...)), produit des cellules de valeurs égales qui ne partagent pas l’identité d’objet par slot de dictionary observée avec le codec Python.
  • Les colonnes Nullable(Tuple(...)) comportant un ou plusieurs elements sont décodées correctement par le codec Rust. Le codec Python interprète mal ce layout ; le résultat Rust constitue donc le comportement de référence. Les deux codecs prennent en charge Nullable(Tuple()).
  • rust_strict rejette les options de requête que le chemin Rust n’implémente pas, telles que des query_formats custom par requête, plutôt que de modifier silencieusement le comportement.
  • Les erreurs de conversion et de validation lors d’une insertion Rust peuvent lever DataError là où le codec Python lève ValueError, et le texte du message peut différer. Exemples : valeurs Time et Time64 invalides, adresses IPv6, dimensions QBit, longueurs FixedString, chaînes Float64 et tentatives d’insertion d’elements dans une colonne Tuple().
  • L’encodeur Rust rejette b"" pour FixedString(N) ainsi que les chaînes numériques telles que "2" pour les colonnes integer. Le codec Python, lui, complète par des zéros une valeur FixedString vide et convertit les chaînes numériques.
  • Le codec Rust décode certaines valeurs de variante partagée Dynamic vers leurs types Python là où le codec Python laisse la valeur en raw bytes. Par exemple, un Decimal stocké peut être renvoyé comme decimal.Decimal par Rust et comme valeur binaire par Python.
  • Pour les valeurs Date dans le stockage partagé Dynamic, query_np et query_df renvoient des cellules datetime.date avec le codec Rust et des cellules numpy.datetime64 avec le codec Python. Les deux codecs renvoient datetime.date depuis query.