Skip to main content
ClickHouse Connect puede decodificar los resultados de las consultas y codificar los inserts con un códec de Rust compilado en lugar de la implementación predeterminada en Python y Cython. El códec de Rust se aplica únicamente al tráfico FORMAT Native gestionado por el client, lo que abarca query, query_np, query_df, sus variantes de streaming por bloques y por filas, y los inserts, incluido insert_df. Los métodos de Arrow usan FORMAT Arrow y no se ven afectados, al igual que las consultas sin procesar, los inserts sin procesar y los formatos que no son Native. El códec es experimental y de uso opcional. El códec de Python sigue siendo el predeterminado.

Instalación

El códec compilado se distribuye como un paquete wheel independiente llamado clickhouse-connect-core, que proporciona el módulo de extensión _ch_core. Instale el códec junto con las integraciones que use su aplicación:
Se publican wheels del núcleo para CPython 3.10 a 3.14 y para CPython 3.14t free-threaded en Linux, macOS y Windows. En Python free-threaded, importar la extensión del núcleo no vuelve a habilitar el GIL. La salida estándar de NumPy y Pandas en Rust no requiere PyArrow ni ningún otro paquete de Arrow. Esto incluye query_np, query_df, sus variantes de streaming y query(..., use_numpy=True). Las consultas de NumPy no requieren Pandas. Agregue el extra async para usar el Client asíncrono. PyArrow sigue siendo necesario para los métodos de Arrow y para el almacenamiento de Pandas basado en Arrow. Instale el extra arrow para usar estas funcionalidades:
La salida extendida de cadenas de Pandas sigue la opción mode.string_storage de Pandas. Cuando PyArrow está instalado, las cadenas usan la conversión de Arrow existente, con validación completa de UTF-8, con cualquiera de los dos backends de almacenamiento. Sin PyArrow, el driver usa la conversión de objetos de Rust y el almacenamiento predeterminado de Pandas usa cadenas de Python. Si se solicita explícitamente el almacenamiento de cadenas de Arrow sin PyArrow, se lanza un error de dependencia. El UTF-8 no válido mantiene la representación hexadecimal existente con cualquiera de los dos backends de almacenamiento. El códec, su empaquetado y su conjunto de dependencias son experimentales. La conversión directa de búfer ahora admite columnas no Nullable de enteros de 8 a 64 bits, Float32/64, Boolean, BFloat16, Interval, Date, Date32, DateTime y DateTime64. Las columnas escalares Time y Time64, incluidos los valores Nullable, también usan búferes directos, al igual que BFloat16 Nullable y la salida extendida de Pandas de enteros, flotantes e Interval Nullable. BFloat16 conserva la salida float32 y su política actual de valores faltantes Float32 de Pandas. Los valores Interval siguen siendo recuentos con signo en su unidad declarada. La conversión temporal conserva las unidades declaradas y las reglas de zona horaria existentes. Los alias Nullable de SimpleAggregateFunction de Time64 ahora devuelven duraciones correctas y NaT para los valores NULL. Las columnas escalares Nullable DateTime64(9) también usan búferes para preservar los nanosegundos en la salida de NumPy y Pandas. Las demás fechas y marcas de tiempo Nullable mantienen sus rutas de conversión de objetos. Los arrays de Time/Time64, incluidos los arrays anidados y las hojas Nullable, y LowCardinality(Time), incluidos los valores Nullable, también usan búferes directos. Su anidamiento, sus unidades de duración y su representación de valores nulos no cambian. LowCardinality(Time64) sigue sin ser compatible con el núcleo de Rust. Para las columnas escalares Nullable DateTime64(9), query_df y query_df_stream preservan los nueve dígitos fraccionarios, incluso a través de alias de SimpleAggregateFunction. En Pandas 3, las columnas de fecha y hora de la salida predeterminada ahora usan unidades de nanosegundos en lugar de microsegundos. La inferencia actual del dtype object para bloques compuestos íntegramente por NULL no cambia. Si una zona horaria con nombre situara un valor fuera del rango de nanosegundos de Pandas, esa columna mantiene su conversión de objetos y su comportamiento de rango anteriores. Para estas columnas, los campos de objeto de query_np y query_np_stream ahora contienen celdas numpy.datetime64 en lugar de celdas datetime de Python, con None para los NULL de SQL. Los valores de zonas con nombre usan ticks UTC sin metadata de zona horaria, igual que el códec de Python. Así se preservan los nanosegundos sin necesidad de Pandas. Use query_df si necesita marcas de tiempo con zona horaria. Los demás tipos de celdas temporales Nullable de NumPy no cambian. Las consultas escalares DateTime64 de NumPy y Pandas rechazan con ProgrammingError las precisiones distintas de 0, 3, 6 y 9, incluidas las columnas Nullable y los alias de SimpleAggregateFunction. Si se selecciona un códec de Rust y el módulo compilado no está instalado, la creación del Client lanza un NotSupportedError que indica este comando de instalación.

Habilitar el códec

Seleccione el códec con la opción de client native_codec:
Valores aceptados: El valor predeterminado también puede definirse mediante el common setting native_codec o la environment variable CLICKHOUSE_CONNECT_NATIVE_CODEC. El orden de precedencia es: primero el keyword argument del client, luego el common setting y, por último, la environment variable. Esta opción se ignora en los clients con interface="chdb", que siempre usan el códec de Python.

Cuándo usar el códec de Rust

El códec de Rust resulta más útil para resultados de DataFrame grandes con tipos de texto, de contenedor y complejos, como String, LowCardinality, Map, Array, JSON, Decimal y UUID. También puede beneficiar a streams de bloques grandes de filas y columnas, a cargas de trabajo con consultas concurrentes y a inserciones masivas. En resultados pequeños y consultas limitadas por la red, la diferencia puede ser mínima. Los resultados numéricos planos ya utilizan rutas masivas eficientes en el códec de Python, por lo que el beneficio también puede ser menor. Los búferes directos evitan arrays intermedios de Arrow para los tipos compatibles. El ensamblado final de resultados de NumPy y Pandas aún puede reservar memoria y copiar datos, por lo que esto no hace que todos los resultados de consultas sean zero-copy. Sin PyArrow, la conversión de cadenas en Rust construye objetos de cadena de Python a partir de cada bloque decodificado. Con los tamaños de bloque predeterminados, el pico de memoria para resultados grandes de tipo String es similar al del códec de Python. Los bloques muy grandes, como un único bloque que contiene un millón de filas, mantienen en memoria todo el bloque decodificado mientras se construyen sus cadenas y pueden alcanzar un pico de memoria mayor que el del códec de Python. Para resultados grandes de Pandas con StringDtype, instala el extra arrow y usa el almacenamiento de cadenas de Arrow. Pandas 3 selecciona el almacenamiento de Arrow de forma predeterminada cuando PyArrow está instalado. Pandas 2 requiere pd.options.mode.string_storage = "pyarrow". Las llamadas a query() almacenadas temporalmente en el búfer sobre resultados muy anchos o totalmente numéricos pueden ser, por ahora, más lentas y consumir más memoria máxima con el códec de Rust. Para esas cargas de trabajo, es preferible usar query_df, query_row_block_stream o query_column_block_stream. Para NumPy y Pandas, query_np_stream y query_df_stream también pueden reducir el pico de memoria. Procesa y libera cada bloque en lugar de acumular todo el stream. El uso de memoria sigue dependiendo del tamaño de cada bloque, por lo que hacer streaming de un único bloque grande no ofrece la misma ventaja. Haz un benchmark de tu propia carga de trabajo antes de adoptar el códec. Usa native_codec="rust_strict" mientras mides, de modo que una opción no soportada o una dependencia faltante lance un error en lugar de redirigir silenciosamente la consulta a Python.

Reglas de fallback

Las decisiones de fallback se toman antes de leer o enviar ningún byte, por lo que nunca se produce un cambio de códec a mitad del stream. En las queries, la elección ocurre antes de consumir el response body. En los inserts, el codificador de Rust solo se selecciona cuando todos los tipos de columna son compatibles; de lo contrario, todo el insert se ejecuta con el códec de Python. Cuando naive_datetime_insert="server" está activo, rust deriva al códec de Python cualquier insert que contenga alguna columna DateTime o DateTime64, de modo que se aplique el timezone declarado de la columna o el server timezone. rust_strict rechaza esa combinación. El modo predeterminado naive_datetime_insert="local" sigue utilizando el codificador de Rust. Las queries de metadata internas del driver, incluidas las sentencias de reflection del dialect de SQLAlchemy, siempre usan el códec de Python, de forma silenciosa, en todos los modos. Los payloads Native malformados que detecta el códec de Rust generan DataError.

Control de versiones

clickhouse-connect-core se versiona de forma independiente de clickhouse-connect. El extra rust requiere clickhouse-connect-core>=0.2.1,<0.3. El driver comprueba tanto la versión de la API de vinculación como la capacidad de búfer de columnas cuando se selecciona un códec de Rust. El wheel publicado de core 0.2.0 no incluye la capacidad de búfer de columnas. Si el wheel instalado es incompatible, la creación del Client lanza un NotSupportedError tanto para rust como para rust_strict, con este comando de upgrade:
Las correcciones y las mejoras del rendimiento en el códec compilado se publican como versiones de clickhouse-connect-core y pueden incorporarse actualizando a un wheel de core compatible. Los cambios en la integración de Rust del driver requieren actualizar clickhouse-connect.

Diferencias de comportamiento conocidas

El códec de Rust busca la paridad celda a celda con el códec de Python. Se conocen las siguientes diferencias.
  • Los resultados de query_np y query_df para columnas Variant contienen objetos Python simples en lugar de valores escalares de numpy. Los valores son iguales, pero los tipos de celda difieren.
  • Los valores Dynamic que contienen Time64 se materializan como datetime.timedelta en los resultados de query_np y query_df de Rust. Con las escalas 0, 3, 6 y 9, los valores coinciden con las celdas numpy.timedelta64 del códec de Python, pero los tipos de celda difieren. En las demás escalas, el códec de Rust devuelve datetime.timedelta, mientras que el de Python lanza ProgrammingError porque NumPy no dispone de una unidad equivalente. La metadata de los miembros de Dynamic no se expone al driver tras la decodificación en Rust, por lo que conviene usar native_codec="python" cuando se necesiten tipos de celda de NumPy o la validación de escalas no admitidas.
  • En query_df, el códec de Python puede convertir a cadena los valores compuestos almacenados en los shared data de JSON. El códec de Rust devuelve objetos decodificados, lo que coincide con los resultados de query_np de ambos códecs.
  • Una alternativa LowCardinality dentro de un contenedor que se materializa por celda, como Array(Variant(...)), produce celdas con valores iguales que no comparten la identidad de objeto por slot de diccionario que sí presenta el códec de Python.
  • Las columnas Nullable(Tuple(...)) con uno o más elementos se decodifican correctamente con el códec de Rust. El códec de Python interpreta mal este layout, por lo que el resultado de Rust es el comportamiento de referencia. Ambos códecs admiten Nullable(Tuple()).
  • rust_strict rechaza las opciones de consulta que la ruta de Rust no implementa, como los query_formats personalizados por consulta, en lugar de alterar el comportamiento de forma silenciosa.
  • Los errores de conversión y validación en las inserciones de Rust pueden lanzar DataError allí donde el códec de Python lanza ValueError, y el texto del mensaje puede diferir. Algunos ejemplos son valores Time y Time64 inválidos, direcciones IPv6, dimensiones de QBit, longitudes de FixedString, cadenas Float64 e intentos de insertar elementos en una columna Tuple().
  • El codificador de Rust rechaza b"" para FixedString(N) y cadenas numéricas como "2" en columnas de enteros. El códec de Python rellena con ceros un valor FixedString vacío y convierte las cadenas numéricas.
  • El códec de Rust decodifica algunos valores de variantes compartidas de Dynamic a sus tipos de Python cuando el códec de Python deja el valor como bytes sin procesar. Por ejemplo, un Decimal almacenado puede devolverse como decimal.Decimal desde Rust y como un valor binario desde Python.
  • En el caso de los valores Date en el almacenamiento compartido de Dynamic, query_np y query_df devuelven celdas datetime.date con el códec de Rust y celdas numpy.datetime64 con el códec de Python. Ambos códecs devuelven datetime.date desde query.