FORMAT Native: это query, query_np, query_df, их варианты с потоковой передачей блоков и строк, а также вставки, включая insert_df. Методы Arrow используют FORMAT Arrow и не затрагиваются, равно как и raw-запросы, raw-вставки и форматы, отличные от Native.
Кодек является экспериментальным и включается явно. По умолчанию используется кодек на Python.
Установка
Скомпилированный кодек поставляется в виде отдельного wheel-пакетаclickhouse-connect-core, содержащего модуль расширения _ch_core. Установите кодек вместе с интеграциями, которые использует ваше приложение:
query_np, query_df, их потоковым вариантам, а также к query(..., use_numpy=True). Для запросов NumPy пакет Pandas не нужен. Чтобы использовать асинхронный клиент, добавьте дополнительный модуль async.
PyArrow по-прежнему необходим для методов Arrow и хранилища Pandas на базе Arrow. Чтобы использовать эти возможности, установите дополнительный модуль arrow:
mode.string_storage. Если установлен PyArrow, строки преобразуются существующим механизмом Arrow с полной проверкой UTF-8 для любого бэкенда хранилища. Без PyArrow драйвер использует объектное преобразование на Rust, а хранилище Pandas по умолчанию использует строки Python. Явный запрос хранилища строк Arrow без установленного PyArrow вызывает ошибку зависимости. Некорректные данные UTF-8, как и прежде, отображаются в шестнадцатеричном виде при любом бэкенде хранилища.
Кодек, его упаковка и набор зависимостей являются экспериментальными. Прямое преобразование буферов теперь поддерживает столбцы с типами non-nullable 8–64-битных целых чисел, Float32/64, Boolean, BFloat16, Interval, Date, Date32, DateTime и DateTime64. Скалярные столбцы Time и Time64, в том числе со значениями Nullable, также используют прямые буферы — как и Nullable BFloat16, а также вывод целых чисел, чисел с плавающей точкой и Interval с типом Nullable в расширенном режиме Pandas. Для BFloat16 сохраняются вывод в float32 и прежняя политика обработки отсутствующих значений Pandas Float32. Значения Interval по-прежнему представляются знаковыми счётчиками в объявленной единице измерения. При преобразовании временных типов сохраняются объявленные единицы и прежние правила обработки часовых поясов. Nullable-псевдонимы SimpleAggregateFunction для Time64 теперь возвращают корректные длительности и NaT для значений NULL. Скалярные столбцы Nullable DateTime64(9) также используют буферы, что позволяет сохранять наносекунды в выводе NumPy и Pandas. Для остальных дат и временных меток с типом Nullable сохраняются прежние пути объектного преобразования.
Массивы Time/Time64, включая вложенные массивы и листовые элементы Nullable, а также LowCardinality(Time), в том числе со значениями Nullable, также используют прямые буферы. Вложенность, единицы длительности и представление NULL для них не меняются. LowCardinality(Time64) по-прежнему не поддерживается ядром на Rust.
Для скалярных столбцов Nullable DateTime64(9) функции query_df и query_df_stream сохраняют все девять знаков дробной части, в том числе при использовании псевдонимов SimpleAggregateFunction. В Pandas 3 столбцы даты и времени в выводе по умолчанию теперь используют наносекунды вместо микросекунд. Прежний вывод типов с объектным dtype для блоков, состоящих только из NULL, не меняется. Если из-за именованного часового пояса значение выходит за пределы наносекундного диапазона Pandas, для этого столбца сохраняются прежнее объектное преобразование и прежнее поведение при выходе за диапазон.
Для этих столбцов объектные поля в query_np и query_np_stream теперь содержат ячейки numpy.datetime64 вместо ячеек Python datetime, а для SQL NULL — None. Значения с именованным часовым поясом представляются тиками UTC без метаданных часового пояса — так же, как в кодеке на Python. Это позволяет сохранять наносекунды без Pandas. Если вам нужны временные метки с учётом часового пояса, используйте query_df. Типы ячеек NumPy для остальных временных столбцов с типом Nullable не меняются.
Запросы NumPy и Pandas для скалярных DateTime64 с точностью, отличной от 0, 3, 6 и 9, завершаются ошибкой ProgrammingError — в том числе для столбцов с типом Nullable и псевдонимов SimpleAggregateFunction.
Если выбран кодек на Rust, а скомпилированный модуль не установлен, при создании клиента возникает ошибка NotSupportedError с указанием этой команды установки.
Включение кодека
Выберите кодек с помощью клиентской опцииnative_codec:
Значение по умолчанию также можно задать через общую настройку
native_codec или переменную окружения CLICKHOUSE_CONNECT_NATIVE_CODEC. Наивысший приоритет имеет именованный аргумент клиента, затем общая настройка и, наконец, переменная окружения.
Для клиентов с interface="chdb" этот параметр игнорируется — они всегда используют кодек на Python.
Когда использовать кодек на Rust
Кодек на Rust наиболее полезен для больших результатов в виде DataFrame с текстовыми, контейнерными и составными типами, такими какString, LowCardinality, Map, Array, JSON, Decimal и UUID. Он также может ускорить работу с большими потоками блоков строк и столбцов, параллельными рабочими нагрузками из запросов и массовыми вставками.
Для небольших результатов и запросов, ограниченных пропускной способностью сети, разница может быть незначительной. Простые числовые результаты уже обрабатываются эффективными пакетными путями в кодеке на Python, поэтому выигрыш для них тоже будет меньше.
Прямые буферы позволяют обойтись без промежуточных массивов Arrow для поддерживаемых типов. При итоговой сборке результата NumPy и Pandas по-прежнему возможны выделение памяти и копирование данных, поэтому не каждый результат запроса становится zero-copy.
Без PyArrow преобразование строк в Rust создаёт строковые объекты Python из каждого декодированного блока. При размерах блоков по умолчанию пиковое потребление памяти для больших результатов типа String сопоставимо с кодеком на Python. Очень большие блоки, например один блок с миллионом строк, удерживают в памяти весь декодированный блок, пока создаются его строки, и могут требовать больше пиковой памяти, чем кодек на Python. Для больших результатов Pandas StringDtype установите дополнительный модуль arrow и используйте хранение строк в Arrow. Pandas 3 по умолчанию выбирает хранение в Arrow, если установлен PyArrow. Для Pandas 2 требуется pd.options.mode.string_storage = "pyarrow".
Буферизованные вызовы query() для очень широких или полностью числовых результатов сейчас могут работать медленнее и давать более высокое пиковое потребление памяти при использовании кодека на Rust. Для таких рабочих нагрузок предпочтительнее query_df, query_row_block_stream или query_column_block_stream. Для NumPy и Pandas снизить пиковое потребление памяти также помогают query_np_stream и query_df_stream. Обрабатывайте и освобождайте каждый блок, а не накапливайте весь поток. Потребление памяти по-прежнему зависит от размера каждого блока, поэтому стриминг одного большого блока не даёт такого же преимущества.
Прежде чем переходить на этот кодек, протестируйте его на собственной рабочей нагрузке. При замерах используйте native_codec="rust_strict", чтобы неподдерживаемая опция или отсутствующая зависимость приводили к исключению, а не к незаметному перенаправлению запроса в Python.
Правила fallback
Решение о fallback принимается до того, как будут прочитаны или отправлены какие-либо байты, поэтому переключение кодека посреди потока невозможно. Для запросов выбор делается до чтения тела ответа. Для вставок кодировщик на Rust выбирается только в том случае, если поддерживаются все типы столбцов; иначе вся вставка выполняется на кодеке на Python. Когда активен режимnaive_datetime_insert="server", режим rust направляет вставку, содержащую хотя бы один столбец типа DateTime или DateTime64, на кодек на Python, чтобы применялся объявленный timezone столбца или server timezone. rust_strict отклоняет такое сочетание. Режим по умолчанию naive_datetime_insert="local" по-прежнему использует кодировщик на Rust.
Внутренние запросы драйвера к метаданным, включая команды reflection диалекта SQLAlchemy, всегда используют кодек на Python — незаметно для пользователя и во всех режимах.
Некорректные полезные нагрузки Native, обнаруженные кодеком Rust, приводят к ошибке DataError.
Versioning
clickhouse-connect-core версионируется независимо от clickhouse-connect. Дополнительный модуль rust требует clickhouse-connect-core>=0.2.1,<0.3. При выборе кодека на Rust драйвер проверяет как версию binding API, так и поддержку буфера столбцов. В опубликованном wheel-пакете core 0.2.0 поддержка буфера столбцов отсутствует. Если установленный wheel-пакет несовместим, при создании клиента как для rust, так и для rust_strict возникает NotSupportedError со следующей командой обновления:
clickhouse-connect-core, и их можно получить, обновив wheel-пакет core до совместимой версии. Изменения в интеграции драйвера с Rust требуют обновления clickhouse-connect.
Известные различия в поведении
Кодек на Rust нацелен на побитовое совпадение с кодеком на Python вплоть до каждой ячейки. Ниже перечислены известные различия.- Результаты
query_npиquery_dfдля столбцовVariantсодержат обычные объекты Python, а не скалярные значения numpy. Сами значения совпадают, различаются типы ячеек. - Значения
Dynamic, содержащиеTime64, материализуются какdatetime.timedeltaв результатахquery_npиquery_dfкодека на Rust. При scale 0, 3, 6 и 9 значения совпадают с ячейкамиnumpy.timedelta64кодека на Python, но типы ячеек различаются. При других значениях scale кодек на Rust возвращаетdatetime.timedelta, тогда как кодек на Python выбрасываетProgrammingError, поскольку в NumPy нет подходящей единицы измерения. Метаданные членов Dynamic после декодирования на Rust драйверу не передаются, поэтому используйтеnative_codec="python", если нужны типы ячеек NumPy или проверка неподдерживаемых значений scale. - Для
query_dfкодек на Python может преобразовывать в строки составные значения, хранящиеся в общих данных JSON. Кодек на Rust возвращает декодированные объекты, что соответствует результатамquery_npобоих кодеков. - Альтернатива
LowCardinalityвнутри контейнера, который материализуется поячеечно, напримерArray(Variant(...)), даёт равные по значению ячейки, которые не разделяют идентичность объекта на слот словаря, характерную для кодека на Python. - Столбцы
Nullable(Tuple(...))с одним или несколькими элементами кодек на Rust декодирует корректно. Кодек на Python читает такую структуру неверно, поэтому эталонным считается результат Rust. Оба кодека поддерживаютNullable(Tuple()). rust_strictотклоняет параметры запроса, не реализованные в Rust-пути, например пользовательскиеquery_formatsдля отдельного запроса, вместо того чтобы незаметно менять поведение.- Ошибки преобразования и валидации при вставке в Rust могут приводить к
DataErrorтам, где кодек на Python выбрасываетValueError, при этом текст сообщения может отличаться. Например: недопустимые значенияTimeиTime64, IPv6-адреса, размерности QBit, длиныFixedString, строкиFloat64, а также попытки вставить элементы в столбецTuple(). - Кодировщик на Rust отклоняет
b""дляFixedString(N)и числовые строки вида"2"для целочисленных столбцов. Кодек на Python дополняет пустое значениеFixedStringнулями и приводит числовые строки к нужному типу. - Кодек на Rust декодирует некоторые значения общих вариантов
Dynamicв соответствующие типы Python, тогда как кодек на Python оставляет значение в виде сырых байтов. Например, сохранённое значениеDecimalиз Rust вернётся какdecimal.Decimal, а из Python — как двоичное значение. - Для значений
Dateв общем хранилищеDynamicметодыquery_npиquery_dfвозвращают ячейкиdatetime.dateпри использовании кодека на Rust и ячейкиnumpy.datetime64при использовании кодека на Python. Изqueryоба кодека возвращаютdatetime.date.