Skip to main content
O ClickHouse Connect pode decodificar resultados de consultas e codificar inserções com um codec Rust compilado em vez da implementação padrão em Python e Cython. O codec Rust se aplica apenas ao tráfego FORMAT Native gerenciado pelo cliente, que abrange query, query_np, query_df, suas variantes de streaming por block e por linha, e as inserções, incluindo insert_df. Os métodos Arrow usam FORMAT Arrow e não são afetados, assim como as consultas raw, as inserções raw e os formatos não Native. O codec é experimental e de uso opcional. O codec Python continua sendo o padrão.

Instalação

O codec compilado é distribuído como um wheel separado, chamado clickhouse-connect-core, que fornece o módulo de extensão _ch_core. Instale o codec junto com as integrações usadas pela sua aplicação:
Os wheels do núcleo são publicados para CPython 3.10 a 3.14 e para o CPython 3.14t com free-threading no Linux, macOS e Windows. No Python com free-threading, importar a extensão do núcleo não reabilita o GIL. A saída comum de NumPy e Pandas em Rust não exige PyArrow nem qualquer outro pacote Arrow. Isso inclui query_np, query_df, suas variantes de streaming e query(..., use_numpy=True). Consultas NumPy não exigem Pandas. Adicione o extra async para usar o cliente assíncrono. O PyArrow continua sendo necessário para os métodos Arrow e para o armazenamento Pandas com Arrow como backend. Instale o extra arrow para usar esses recursos:
A saída estendida de strings do Pandas segue a opção mode.string_storage do Pandas. Quando o PyArrow está instalado, as strings usam a conversão Arrow existente, com validação UTF-8 completa, em qualquer um dos backends de armazenamento. Sem o PyArrow, o driver usa a conversão de objetos em Rust, e o armazenamento padrão do Pandas usa strings Python. Solicitar explicitamente o armazenamento de strings Arrow sem o PyArrow gera um erro de dependência. UTF-8 inválido mantém a renderização hexadecimal existente em qualquer um dos backends de armazenamento. O codec, seu empacotamento e seu conjunto de dependências são experimentais. A conversão direta de buffers agora trata colunas de inteiros não anuláveis de 8 a 64 bits, Float32/64, Boolean, BFloat16, Interval, Date, Date32, DateTime e DateTime64. Colunas escalares Time e Time64, incluindo valores anuláveis, também usam buffers diretos, assim como BFloat16 anulável e a saída estendida do Pandas para inteiros, floats e Interval anuláveis. BFloat16 mantém a saída float32 e sua política atual de valores ausentes Float32 do Pandas. Valores Interval continuam sendo contagens com sinal em sua unidade declarada. A conversão temporal mantém as unidades declaradas e as regras de fuso horário existentes. Aliases SimpleAggregateFunction anuláveis de Time64 agora retornam durações corretas e NaT para valores NULL. Colunas escalares anuláveis DateTime64(9) também usam buffers para preservar nanossegundos na saída do NumPy e do Pandas. As demais datas e timestamps anuláveis mantêm seus caminhos de conversão de objetos. Arrays de Time/Time64, incluindo arrays aninhados e folhas anuláveis, e LowCardinality(Time), incluindo valores anuláveis, também usam buffers diretos. O aninhamento, as unidades de duração e as representações de nulos permanecem inalterados. LowCardinality(Time64) continua sem suporte no núcleo em Rust. Para DateTime64(9) escalar anulável, query_df e query_df_stream preservam todos os nove dígitos fracionários, inclusive por meio de aliases SimpleAggregateFunction. No Pandas 3, colunas datetime na saída padrão agora usam unidades de nanossegundos em vez de microssegundos. A inferência atual de dtype object para blocos totalmente NULL permanece inalterada. Se um fuso horário nomeado colocar um valor fora do intervalo de nanossegundos do Pandas, essa coluna mantém a conversão de objetos e o comportamento de intervalo anteriores. Para essas colunas, os campos de objeto de query_np e query_np_stream agora contêm células numpy.datetime64 em vez de células datetime do Python, com None para NULL do SQL. Valores com fuso nomeado usam ticks UTC sem metadados de fuso horário, assim como o codec Python. Isso preserva os nanossegundos sem exigir o Pandas. Use query_df se precisar de timestamps com fuso horário. Os demais tipos de células temporais anuláveis do NumPy permanecem inalterados. Consultas NumPy e Pandas de DateTime64 escalar rejeitam precisões diferentes de 0, 3, 6 e 9 com ProgrammingError, inclusive em colunas anuláveis e aliases SimpleAggregateFunction. Se um codec Rust for selecionado e o módulo compilado não estiver instalado, a criação do cliente gera um NotSupportedError que informa este comando de instalação.

Habilitando o codec

Selecione o codec com a opção de cliente native_codec:
Valores aceitos: O padrão também pode ser definido pela configuração comum native_codec ou pela variável de ambiente CLICKHOUSE_CONNECT_NATIVE_CODEC. A precedência é: primeiro o keyword argument do cliente, depois a configuração comum e, por fim, a variável de ambiente. A opção é ignorada para clientes com interface="chdb", que sempre usam o codec Python.

Quando usar o codec Rust

O codec Rust é mais útil para resultados grandes de DataFrame com tipos de texto, contêineres e tipos complexos como String, LowCardinality, Map, Array, JSON, Decimal e UUID. Ele também pode ajudar em streams de blocos grandes de linhas e colunas, cargas de trabalho com consultas concorrentes e inserts em massa. Resultados pequenos e consultas limitadas pela rede podem apresentar pouca diferença. Resultados numéricos simples já utilizam caminhos em massa eficientes no codec Python, portanto também tendem a se beneficiar menos. Buffers diretos evitam arrays intermediários do Arrow para os tipos compatíveis. A montagem final dos resultados em NumPy e Pandas ainda pode alocar e copiar dados, portanto isso não torna todo resultado de consulta zero-copy. Sem o PyArrow, a conversão de strings no Rust cria objetos string do Python a partir de cada bloco decodificado. Com os tamanhos de bloco padrão, o pico de memória para resultados grandes de String é próximo ao do codec Python. Blocos muito grandes, como um único bloco com um milhão de linhas, mantêm todo o bloco decodificado em memória enquanto suas strings são criadas e podem consumir mais memória de pico do que o codec Python. Para resultados grandes com Pandas StringDtype, instale o extra arrow e use o armazenamento de strings do Arrow. O Pandas 3 seleciona o armazenamento Arrow por padrão quando o PyArrow está instalado. O Pandas 2 requer pd.options.mode.string_storage = "pyarrow". Chamadas query() com buffer sobre resultados muito largos ou totalmente numéricos podem, atualmente, ser mais lentas e consumir mais memória de pico com o codec Rust. Para essas cargas de trabalho, prefira query_df, query_row_block_stream ou query_column_block_stream. Para NumPy e Pandas, query_np_stream e query_df_stream também podem reduzir o pico de memória. Processe e libere cada bloco em vez de acumular todo o stream. O uso de memória ainda depende do tamanho de cada bloco, portanto fazer streaming de um único bloco grande não traz o mesmo benefício. Faça um benchmark da sua própria carga de trabalho antes de adotar o codec. Use native_codec="rust_strict" durante as medições para que uma opção sem suporte ou uma dependência ausente gere um erro, em vez de encaminhar silenciosamente a consulta para o Python.

Regras de fallback

As decisões de fallback são tomadas antes que qualquer byte seja lido ou enviado, portanto nunca ocorre troca de codec no meio do stream. Para queries, a escolha acontece antes de o response body ser consumido. Para inserções, o codificador Rust só é selecionado quando todos os tipos de coluna têm suporte; caso contrário, a inserção inteira é executada no codec Python. Quando naive_datetime_insert="server" está ativo, o rust encaminha ao codec Python qualquer inserção que contenha uma coluna DateTime ou DateTime64, de modo que a timezone declarada da coluna ou a server timezone seja aplicada. Já o rust_strict rejeita essa combinação. O modo padrão naive_datetime_insert="local" continua usando o codificador Rust. Queries de metadados internas do driver, incluindo as instruções de reflexão do dialect do SQLAlchemy, sempre usam o codec Python, de forma silenciosa, em todos os modos. Payloads Native malformados detectados pelo codec Rust geram DataError.

Versioning

O clickhouse-connect-core é versionado de forma independente do clickhouse-connect. O extra rust requer clickhouse-connect-core>=0.2.1,<0.3. Quando um codec Rust é selecionado, o driver verifica tanto a versão da API de binding quanto a capability de buffer de coluna. A wheel do core 0.2.0 publicada não tem a capability de buffer de coluna. Se a wheel instalada for incompatível, a criação do cliente gera um NotSupportedError tanto para rust quanto para rust_strict, com este comando de upgrade:
Correções e melhorias de desempenho no codec compilado são publicadas como lançamentos do clickhouse-connect-core e podem ser obtidas com um upgrade para uma wheel do core compatível. Alterações na integração Rust do driver exigem um upgrade do clickhouse-connect.

Diferenças de comportamento conhecidas

O codec Rust busca paridade célula a célula com o codec Python. As seguintes diferenças são conhecidas.
  • Os resultados de query_np e query_df para colunas Variant contêm objetos Python simples em vez de valores escalares do numpy. Os valores são iguais, mas os tipos das células diferem.
  • Valores Dynamic que contêm Time64 são materializados como datetime.timedelta nos resultados de query_np e query_df do Rust. Nas escalas 0, 3, 6 e 9, os valores são iguais às células numpy.timedelta64 do codec Python, mas os tipos das células diferem. Nas demais escalas, o codec Rust retorna datetime.timedelta, enquanto o codec Python gera ProgrammingError, pois o NumPy não possui uma unidade correspondente. Os metadados dos membros de Dynamic não são expostos ao driver após a decodificação em Rust; portanto, use native_codec="python" quando forem necessários os tipos de célula do NumPy ou a validação de escalas sem suporte.
  • Em query_df, o codec Python pode converter em string valores compostos armazenados em shared data de JSON. O codec Rust retorna objetos decodificados, o que corresponde aos resultados de query_np de ambos os codecs.
  • Uma alternativa LowCardinality dentro de um contêiner que se materializa por célula, como Array(Variant(...)), produz células com valores iguais que não compartilham a identidade de objeto por slot de dicionário apresentada pelo codec Python.
  • Colunas Nullable(Tuple(...)) com um ou mais elementos são decodificadas corretamente no codec Rust. O codec Python interpreta esse layout de forma incorreta, e o resultado do Rust é o comportamento de referência. Ambos os codecs suportam Nullable(Tuple()).
  • rust_strict rejeita opções de consulta que o caminho Rust não implementa, como query_formats customizados por consulta, em vez de alterar o comportamento silenciosamente.
  • Erros de conversão e de validação de insert no Rust podem gerar DataError onde o codec Python gera ValueError, e o texto da mensagem pode diferir. Entre os exemplos estão valores Time e Time64 inválidos, endereços IPv6, dimensões de QBit, comprimentos de FixedString, strings Float64 e tentativas de inserir elementos em uma coluna Tuple().
  • O codificador Rust rejeita b"" para FixedString(N) e strings numéricas como "2" em colunas de inteiros. O codec Python preenche com zeros um valor FixedString vazio e converte strings numéricas.
  • O codec Rust decodifica alguns valores de variante compartilhada de Dynamic para seus tipos Python nos casos em que o codec Python mantém o valor como raw bytes. Por exemplo, um Decimal armazenado pode ser retornado como decimal.Decimal no Rust e como um valor binário no Python.
  • Para valores Date no armazenamento compartilhado de Dynamic, query_np e query_df retornam células datetime.date com o codec Rust e células numpy.datetime64 com o codec Python. Ambos os codecs retornam datetime.date a partir de query.