Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
e1e8283
rust core binding poc checkpoint
joe-clickhouse Jun 9, 2026
b1b16af
Merge remote-tracking branch 'origin/main' into joe/rust-core
joe-clickhouse Jun 10, 2026
c6be4a0
rust core binding POC
joe-clickhouse Jun 11, 2026
8ed7ea6
add lowcardinality binding support
joe-clickhouse Jun 30, 2026
1c14561
add enum8/16 binding support
joe-clickhouse Jul 1, 2026
5b69cef
implement rust core binding insert path support
joe-clickhouse Jul 6, 2026
69a9769
add native_codec rust codec selector
joe-clickhouse Jul 6, 2026
cf4abf1
implement codec choice
joe-clickhouse Jul 7, 2026
8b1cf02
optimize and harden rust codec read and insert paths for fixed-width …
joe-clickhouse Jul 7, 2026
f79f678
optimization pass for reads and writes
joe-clickhouse Jul 7, 2026
4ba53d9
implement array(t) encode and decode
joe-clickhouse Jul 8, 2026
9e52d66
array(t) optimization pass
joe-clickhouse Jul 8, 2026
6f13d6c
implement map and tuple decode
joe-clickhouse Jul 8, 2026
6ff2f19
implement map and tuple encode and decode
joe-clickhouse Jul 9, 2026
d1b139f
decompress during not after in rust buffer path
joe-clickhouse Jul 9, 2026
fab9b3e
implement wide ints support
joe-clickhouse Jul 10, 2026
65fed08
implement time/time64 types in binding
joe-clickhouse Jul 10, 2026
8902bdb
add support for geo types and simpleagg
joe-clickhouse Jul 13, 2026
b5e1b45
rust codec df-parity fix
joe-clickhouse Jul 13, 2026
03a6cf6
simpagg and geo fix/tests
joe-clickhouse Jul 13, 2026
6095c55
add support for interval types in rust binding
joe-clickhouse Jul 14, 2026
ed04eea
rust binding bfloat16 support
joe-clickhouse Jul 14, 2026
4f5b253
implement Nothing type
joe-clickhouse Jul 14, 2026
9867c33
implement aggregatefunction support
joe-clickhouse Jul 14, 2026
e2b6bc0
add some tests
joe-clickhouse Jul 15, 2026
09edd05
implement variant encode and decode in rust binding
joe-clickhouse Jul 15, 2026
8f578bf
implement dynamic type in rust binding
joe-clickhouse Jul 16, 2026
4729167
initial json type binding support
joe-clickhouse Jul 17, 2026
62e1780
json perf optimization pass
joe-clickhouse Jul 17, 2026
3ad3627
implement geometry type in binding
joe-clickhouse Jul 17, 2026
ab0780d
temp: reject qbit
joe-clickhouse Jul 17, 2026
7b46187
re-layout of rust binding phase 1
joe-clickhouse Jul 17, 2026
01397bb
re-layout of rust binding phase 2
joe-clickhouse Jul 17, 2026
c38d26a
re-layout of rust binding phase 3
joe-clickhouse Jul 17, 2026
2693837
docs update to match new binding layout
joe-clickhouse Jul 17, 2026
d35859f
implement qbit support in binding
joe-clickhouse Jul 17, 2026
96c4f35
rust codec correctness and performance fixes for release
joe-clickhouse Jul 20, 2026
1cd4d1f
review fixes for tz gating, buffered results, and enum nan handling
joe-clickhouse Jul 21, 2026
f71d142
update changelog
joe-clickhouse Jul 21, 2026
b0272e0
Merge branch 'main' into joe/rust-core-binding-poc
joe-clickhouse Jul 21, 2026
2fee9c3
Merge branch 'main' into joe/rust-core-binding-poc
joe-clickhouse Jul 22, 2026
5cc5722
distribute the rust codec as a separate clickhouse-connect-core wheel
joe-clickhouse Jul 23, 2026
5e6a64b
Merge remote-tracking branch 'origin/main' into joe/rust-core-binding…
joe-clickhouse Jul 23, 2026
7bf6350
add PyPI metadata for the clickhouse-connect-core wheel
joe-clickhouse Jul 23, 2026
2daa93b
reference the rust codec from the driver docs
joe-clickhouse Jul 23, 2026
526e056
prune binding arch, remove stale bench results files
joe-clickhouse Jul 23, 2026
68be6ad
add types
joe-clickhouse Jul 23, 2026
2289d01
fix cast in tests
joe-clickhouse Jul 24, 2026
1cd652b
pin rust toolchain ver, and fix clippy
joe-clickhouse Jul 24, 2026
cb0810e
patch core presence in test
joe-clickhouse Jul 24, 2026
5c1c2b1
Merge remote-tracking branch 'origin/main' into joe/rust-core-binding…
joe-clickhouse Aug 10, 2026
afdde45
fix rust Time df dtype parity across pandas majors
joe-clickhouse Aug 10, 2026
2019f6f
Merge remote-tracking branch 'origin/main' into joe/rust-core-binding…
joe-clickhouse Aug 11, 2026
287378d
add rust integration tag
joe-clickhouse Aug 11, 2026
40fe26e
fix rust codec performance regressions found in pre-merge benchmarking
joe-clickhouse Aug 12, 2026
7f2d409
bump binding api version
joe-clickhouse Aug 12, 2026
f6aa795
upgrade binding to pyo3 0.29
joe-clickhouse Aug 12, 2026
2e91f84
fix core wheel matrix interpreter selection
joe-clickhouse Aug 12, 2026
45f8191
fix interpreter spec for windows arm64 cross build
joe-clickhouse Aug 12, 2026
3e5a76d
Merge remote-tracking branch 'origin/main' into joe/rust-core-binding…
joe-clickhouse Aug 12, 2026
a2b48ed
build manylinux wheels on the 2_28 image
joe-clickhouse Aug 12, 2026
1513396
release prep for rust binding rc 1.8.0rc1
joe-clickhouse Aug 12, 2026
9ccafae
Merge branch 'main' into joe/rust-core-binding-poc
joe-clickhouse Aug 20, 2026
dd867f5
release prep for v1.8.0rc2 (#982)
joe-clickhouse Aug 20, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .agents/server-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,50 @@ Cite file and class or function names in your answers. Do not cite line numbers,

`src/DataTypes/DataTypeDecimalBase.{cpp,h}`, `src/DataTypes/DataTypesDecimal.h`, `src/DataTypes/Serializations/SerializationDecimal.{cpp,h}`. Stored as fixed-width two's-complement integer (32/64/128/256-bit) with precision and scale carried in the type string.

### QBit

- **Confirmed at v26.3.9.8-lts:** The type implementation is
`src/DataTypes/DataTypeQBit.{cpp,h}` in `DataTypeQBit`.
`DataTypeQBit::createColumn` creates a `ColumnQBit` backed by a tuple containing
16, 32, or 64 `FixedString(ceil(dimension / 8))` columns.
`DataTypeQBit::doGetSerialization` returns `SerializationQBit`. The column
wrapper and bit-plane storage accessors are in `src/Columns/ColumnQBit.{cpp,h}`.
- **Confirmed at v26.3.9.8-lts:** Binary and Native serialization are implemented
by `SerializationQBit` in
`src/DataTypes/Serializations/SerializationQBit.{cpp,h}`. Row-wise binary uses
`serializeBinary`, `deserializeBinary`, `serializeFloatsFromQBit`,
`deserializeFloatsToQBit`, `transposeBits`, and `untransposeBitPlane`. Its wire
value is a VarUInt dimension followed by that many BFloat16, Float32, or Float64
values.
- **Confirmed at v26.3.9.8-lts:** Native uses
`SerializationQBit::serializeBinaryBulkWithMultipleStreams` and
`deserializeBinaryBulkWithMultipleStreams`, reached through
`NativeWriter::writeData` and `NativeReader::readData` in
`src/Formats/NativeWriter.cpp` and `src/Formats/NativeReader.cpp`. These methods
delegate to the nested `SerializationTuple` implementation in
`src/DataTypes/Serializations/SerializationTuple.cpp`; each bit plane is emitted
as raw fixed-width bytes by `SerializationFixedString::serializeBinaryBulk` in
`src/DataTypes/Serializations/SerializationFixedString.cpp`. Native therefore
carries bit-transposed planes, not row-wise float arrays.
- **Confirmed at v26.3.9.8-lts:** The binary type-schema encoding used by Dynamic
and related self-describing serializations is in
`src/DataTypes/DataTypesBinaryEncoding.{cpp,h}`. QBit uses type tag `0x36`,
followed by the encoded element type and a VarUInt dimension.
- **Confirmed at v26.3.9.8-lts:** Focused tests are
`src/DataTypes/Serializations/tests/gtest_qbit_serialization.cpp`
(`QBitSerialization.FieldBinarySerializationFloat32` and
`QBitSerialization.RejectInvalidElementType`) and
`tests/queries/0_stateless/03371_qbit_read_write.{sh,reference}`, which exercises
text, RowBinary, and Native round trips.
- **Confirmed at v26.3.9.8-lts:** Additional coverage is in
`tests/queries/0_stateless/03363_qbit_create_insert_select.{sql,reference}` for all
three element widths and byte-aligned/non-byte-aligned dimensions,
`03368_qbit_subcolumns.{sql,reference}` for bit-plane subcolumns,
`03372_qbit_mergetree_1.{sql,reference}` and
`03372_qbit_mergetree_2.{sql,reference}` for stored data,
`03373_qbit_dynamic.{sql,reference}` for Dynamic, and
`03374_qbit_nullable.{sql,reference}` for Nullable.

### Enum8 / Enum16

`src/DataTypes/DataTypeEnum.{cpp,h}`, `src/DataTypes/EnumValues.{cpp,h}`, `src/DataTypes/Serializations/SerializationEnum.{cpp,h}`. Name-to-value map lives in the type string. Wire format is the underlying Int8/Int16.
Expand Down
91 changes: 91 additions & 0 deletions .github/workflows/binding_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
name: 'Rust Binding CI'

on:
pull_request:
branches:
- main
paths:
- 'rust/**'
- 'clickhouse_connect/driver/rustcodec.py'
- 'clickhouse_connect/driver/rustnumpy.py'
- 'tests/integration_tests/test_rust_codec.py'
- '.github/workflows/binding_ci.yml'
push:
branches:
- main
paths:
- 'rust/**'
- 'clickhouse_connect/driver/rustcodec.py'
- 'clickhouse_connect/driver/rustnumpy.py'
- 'tests/integration_tests/test_rust_codec.py'
- '.github/workflows/binding_ci.yml'
workflow_dispatch:

jobs:
rust-checks:
runs-on: ubuntu-latest
name: Cargo fmt + clippy
steps:
- uses: actions/checkout@v6
# pinned so new stable clippy lints don't break unrelated PRs, bump deliberately
- uses: dtolnay/rust-toolchain@1.97.0
with:
components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
with:
workspaces: rust
- name: Format check
working-directory: rust
run: cargo fmt --all -- --check
- name: Clippy (deny warnings)
working-directory: rust
run: cargo clippy --all-targets -- -D warnings

binding-tests:
runs-on: ubuntu-latest
name: Build binding and run codec tests
needs: rust-checks
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Start ClickHouse (latest) in Docker
env:
CLICKHOUSE_CONNECT_TEST_CH_VERSION: latest
COMPOSE_PROJECT_NAME: clickhouse-connect-binding-ci
run: docker compose -f docker-compose.yml up -d --wait clickhouse
- name: Set up Python 3.12
uses: actions/setup-python@v6
with:
python-version: '3.12'
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: rust
- name: Install Test Dependencies
run: |
python -m pip install --upgrade pip
pip install -r tests/test_requirements.txt
pip install maturin
- name: Build cython extensions
run: python setup.py build_ext --inplace
- name: "Add distribution info" # This lets SQLAlchemy find entry points
run: python setup.py develop
- name: Build and install the clickhouse-connect-core wheel
run: |
maturin build --release -m rust/ch-core-py/Cargo.toml -o core-wheelhouse
pip install core-wheelhouse/*.whl
- name: Extension smoke check
run: python -c "import _ch_core; print(_ch_core.__version__, _ch_core.BINDING_API_VERSION)"
- name: Run binding tests
run: pytest rust/ch-core-py/tests -n 4
- name: Run unit tests
run: pytest tests/unit_tests -n 4
- name: Run rust codec integration tests
env:
CLICKHOUSE_CONNECT_TEST_DOCKER: 'False'
run: pytest tests/integration_tests/test_rust_codec.py -n 4
- name: Stop ClickHouse
if: ${{ always() && hashFiles('docker-compose.yml') != '' }}
env:
COMPOSE_PROJECT_NAME: clickhouse-connect-binding-ci
run: docker compose -f docker-compose.yml down --volumes --remove-orphans
4 changes: 2 additions & 2 deletions .github/workflows/on_push.yml
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,8 @@ jobs:
"$RUNNER_TEMP/consumer/bin/mypy" --strict --follow-imports=silent consumer_smoke.py
- name: Public type completeness ratchet
# SQLAlchemy must be installed while verifytypes scans the public cc_sqlalchemy annotations.
# The pre-feature baseline is 1120 with that optional surface resolved.
run: python scripts/check_public_types.py clickhouse_connect --max-untyped 1120 --python "$RUNNER_TEMP/consumer/bin/python"
# The merged baseline is 1118 with that optional surface resolved.
run: python scripts/check_public_types.py clickhouse_connect --max-untyped 1118 --python "$RUNNER_TEMP/consumer/bin/python"
- name: SQLAlchemy Select typing smoke test
run: |
cp tests/type_check/sqlalchemy_select_smoke.py "$RUNNER_TEMP"
Expand Down
8 changes: 4 additions & 4 deletions .github/workflows/publish_core.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,17 @@ jobs:
fail-fast: false
matrix:
include:
- { runner: ubuntu-latest, arch: x86_64, libc: manylinux, manylinux: auto }
- { runner: ubuntu-latest, arch: x86_64, libc: manylinux, manylinux: 2_28 }
- { runner: ubuntu-latest, arch: x86_64, libc: musllinux, manylinux: musllinux_1_2 }
- { runner: ubuntu-24.04-arm, arch: aarch64, libc: manylinux, manylinux: auto }
- { runner: ubuntu-24.04-arm, arch: aarch64, libc: manylinux, manylinux: 2_28 }
- { runner: ubuntu-24.04-arm, arch: aarch64, libc: musllinux, manylinux: musllinux_1_2 }
steps:
- uses: actions/checkout@v6
- name: Build wheels
uses: PyO3/maturin-action@v1
with:
manylinux: ${{ matrix.manylinux }}
args: --release -m ${{ env.MATURIN_MANIFEST }} -o wheelhouse --find-interpreter
args: --release -m ${{ env.MATURIN_MANIFEST }} -o wheelhouse -i 3.10 3.11 3.12 3.13 3.14
- uses: actions/upload-artifact@v7
with:
name: core-${{ matrix.libc }}-${{ matrix.arch }}
Expand Down Expand Up @@ -127,7 +127,7 @@ jobs:
uses: PyO3/maturin-action@v1
with:
target: aarch64-pc-windows-msvc
args: --release -m ${{ env.MATURIN_MANIFEST }} -o wheelhouse
args: --release -m ${{ env.MATURIN_MANIFEST }} -o wheelhouse -i python3.10
- uses: actions/upload-artifact@v7
with:
name: core-windows-arm64-3.10
Expand Down
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,10 @@ test.env
# Local ClickHouse server source checkout for agent reference (see AGENTS.md)
/.server-src/
.server-ref

# Rust build artifacts
rust/target/
*.so
*.dylib
/.cargo/config.toml
.claude
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

## UNRELEASED

## 1.8.0rc2, 2026-08-20

Follow-up release candidate to 1.8.0rc1, rebased on 1.7.2 so all bug fixes from that stable release are included. The optional Rust codec itself is unchanged.

## 1.8.0rc1, 2026-08-12

### Improvements

- Added an experimental `native_codec` client option that selects the codec for FORMAT Native query decode and insert encode. `python` is the default and uses the existing codec. `rust` prefers the compiled Rust codec and falls back to the Python codec for unsupported options and types, while `rust_strict` raises instead of falling back. Results and dtypes match the Python codec, and the Arrow methods are unaffected. The compiled codec ships as the separate clickhouse-connect-core wheel, installed with `pip install clickhouse-connect[rust]`. See the rust-codec documentation page for details. This is early access for benchmarking and is not yet a supported path.

## 1.7.2, 2026-08-19

### Bug Fixes
Expand Down Expand Up @@ -63,6 +73,8 @@
- `AsyncClient` initialization no longer overwrites user-supplied session settings with generated defaults. A client created with `settings={'date_time_input_format': 'basic'}` previously had that value replaced by the generated `best_effort` default. User settings now always win, matching the sync client.
- An `AsyncClient` created with both client certificates and an access token now sends the mutual TLS authentication headers and the `Authorization: Bearer` header together, matching the sync client. The certificates previously suppressed the token at construction, while the `token_provider` option re-added its token right after initialization, so the two async token paths disagreed with each other. The server resolves the credential precedence.
- Dict-valued settings such as `additional_table_filters` no longer crash with `DB::Exception: Cannot parse quoted string` when passed through `query()`'s `settings` parameter. The value was rendered with Python's own `str()`/`repr()` of the dict, which mixes single and double quotes and is not valid ClickHouse map-literal syntax; it is now rendered as a properly single-quoted, escaped ClickHouse map literal. Closes [#501](https://github.com/ClickHouse/clickhouse-connect/issues/501).
- Explicit NaN and infinity values in nullable `BFloat16` row inserts are now stored instead of being written as 0.
- Inserting an `Array(Dynamic)` column no longer raises `ZeroDivisionError` when a sampled row holds an empty array. The insert block size estimate now treats an empty sample as minimal instead of dividing by its length.

### Improvements
- Async clients now emit URL query parameters in the same order as the sync client on every request. The parameter names and values are unchanged, so this is only visible to systems that match or sign the exact request URL.
Expand Down
71 changes: 71 additions & 0 deletions DISTRIBUTION_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# Rust Codec Distribution Plan

How the Rust native codec is packaged, versioned, released, and supported across ch-core-rs, the Python binding, and clickhouse-connect.

## Decisions

- Users opt in with a single switch on the client. The Rust path uses the new, correct semantics. Known behavior differences from the Python codec are enumerated and documented. Features the Rust path does not implement fall back to the Python codec.
- ch-core-rs is open source on GitHub. It is not published to crates.io. Consumers depend on it as a git dependency pinned to a release tag. The repo is currently internal. Making it public is in progress and gates the first PyPI release.
- The core repo stays pure Rust with no language-specific dependencies. Each language client owns its binding in its own repo.
- The Python binding ships as a separate PyPI wheel, installed via the `rust` extra. It is not compiled into the clickhouse-connect wheel.
- Long term: deprecation warnings on the Python codec late in 1.x, Rust becomes the only codec in 2.0.

## Artifacts and repos

| Piece | Lives in | Ships as | Releases when |
|---|---|---|---|
| ch-core-rs | own public repo, pure Rust | git tags only | core logic changes |
| ch-core-py binding and the rustcodec.py seam | clickhouse-connect repo | `clickhouse-connect-core` wheel on PyPI, module name `_ch_core` | core repin or binding change |
| clickhouse-connect | clickhouse-connect repo | pure/cython package with a `rust` extra | driver features, seam or floor changes |

One repo can publish two PyPI artifacts. Living in the connect repo does not mean shipping in the connect wheel.

## The two seams

**Rust seam (ch-core-rs into the binding).** Compile time only. The core is statically linked into the extension when the wheel builds. Users never resolve it and no runtime skew is possible. Pin by git tag in ch-core-py's Cargo.toml and commit Cargo.lock for reproducible builds.

**Python seam (`clickhouse-connect-core` wheel and clickhouse-connect).** The only seam users see. clickhouse-connect pins a compatible range, for example `clickhouse-connect-core>=1.2,<1.3`. The pin floor encodes the oldest core wheel this driver knows how to drive. Bump the floor only when connect starts using a new binding capability.

## Packaging

- PyPI name `clickhouse-connect-core`, module name `_ch_core`. The name should be recognizable in a user's pip list.
- Install: `pip install clickhouse-connect[rust]`.
- Build with maturin, one wheel per platform per Python version (cp310 through cp314), matching the main package's matrix.
- Not abi3: the binding's hot paths use non-limited C API (`PyTuple_SET_ITEM`, `PyList_SET_ITEM`, presized dict construction) on purpose. Moving to the limited API would tax exactly the paths the codec exists to accelerate. Revisit only if the wheel matrix becomes a real maintenance cost.
- If the switch is enabled without the wheel installed, raise a clear error naming the install command.
- While ch-core-rs remains non-public, wheel builds need repo access from CI and the sdist cannot build for outside users. Making the repo public precedes the first PyPI release.

## Runtime handshake

- `_ch_core` exports a binding API version constant.
- rustcodec.py checks it at import. Too old raises a legible message naming the required `clickhouse-connect-core` version. Never a crash or silent misbehavior.
- The client's diagnostic output includes the `_ch_core` version alongside the driver version so bug reports arrive with both.

## Release workflows

**Core bugfix.** Fix in ch-core-rs, tag a patch release. In the connect repo bump the git tag in ch-core-py, publish a `clickhouse-connect-core` patch wheel. No clickhouse-connect release. Users run `pip install -U clickhouse-connect[rust]`.

**Transparent core improvement** (faster decode, internal wins). Same as a bugfix but a minor bump. Users get it for free with a wheel upgrade.

**User-facing core feature** (new setting, new type, new capability). Core minor bump, binding exposes it, `clickhouse-connect-core` minor release. Then a clickhouse-connect release that uses it and raises the pin floor. The connect release is the feature's public API.

**Driver-only change.** Normal clickhouse-connect release. The wheel is untouched.

## Issue handling

- Users file everything on clickhouse-connect. The core repo tracker is for maintainers and binding authors.
- If a root cause lands in ch-core-rs, fix it there but keep and close the loop in the original clickhouse-connect issue, noting the `clickhouse-connect-core` version that carries the fix.

## Core repo contract

- Semver on tags. No breaking changes on patch or minor.
- A changelog maintained per release. Downstream bindings in multiple languages will depend on reading it.
- No PyO3, napi, or other language-specific dependencies in the core crate. Language artifacts are built by the binding repos.
- CI tests the crate on supported platforms. It builds no wheels or language artifacts.

## Rollout

1. Next minor: ship the opt-in switch, the `rust` extra, the known-differences documentation, and the fallback behavior.
2. During 1.x: promote the Rust path as it proves out. Wheel-only releases carry core fixes and wins to opted-in users.
3. Late 1.x: DeprecationWarning on the Python codec path.
4. 2.0: Rust codec becomes the only codec. The differences list becomes the documented behavior.
2 changes: 1 addition & 1 deletion clickhouse_connect/_version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
version = "1.7.2"
version = "1.8.0rc2"
4 changes: 4 additions & 0 deletions clickhouse_connect/cc_sqlalchemy/datatypes/sqltypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,10 @@ class MultiLineString(ChSqlaType, UserDefinedType): # type: ignore[misc]
python_type = list


class Geometry(ChSqlaType, UserDefinedType): # type: ignore[misc]
python_type = object


class Date(ChSqlaType, SqlaDate): # type: ignore[misc]
pass

Expand Down
21 changes: 21 additions & 0 deletions clickhouse_connect/common.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import getpass
import logging
import os
import sys
from collections.abc import Sequence
from dataclasses import dataclass
Expand All @@ -7,6 +9,10 @@
from clickhouse_connect._version import version as _version_string
from clickhouse_connect.driver.exceptions import ProgrammingError

logger: logging.Logger = logging.getLogger(__name__)

_NATIVE_CODEC_OPTIONS = ("python", "rust", "rust_strict")


def version() -> str:
return _version_string
Expand Down Expand Up @@ -68,6 +74,17 @@ def _init_common(name: str, options: Sequence[Any], default: Any) -> None:
_common_settings[name] = CommonSetting(name, options, default)


def _native_codec_env_default() -> str:
raw = os.environ.get("CLICKHOUSE_CONNECT_NATIVE_CODEC")
if raw is None:
return "python"
value = raw.strip().lower()
if value in _NATIVE_CODEC_OPTIONS:
return value
logger.warning("Ignoring invalid CLICKHOUSE_CONNECT_NATIVE_CODEC=%r; using 'python'", raw)
return "python"


_init_common("autogenerate_session_id", (True, False), True)
_init_common("autogenerate_query_id", (True, False), True)
_init_common("dict_parameter_format", ("json", "map"), "json")
Expand All @@ -79,6 +96,10 @@ def _init_common(name: str, options: Sequence[Any], default: Any) -> None:
_init_common("readonly", (0, 1), 0) # Deprecated no-op retained for 1.x compatibility
_init_common("send_os_user", (True, False), True)

# Selects the codec for client-managed FORMAT Native query and insert paths. Seeded by
# CLICKHOUSE_CONNECT_NATIVE_CODEC; overridable per client via the native_codec kwarg.
_init_common("native_codec", _NATIVE_CODEC_OPTIONS, _native_codec_env_default())

# Include integration tags (library name/version) in the User-Agent, e.g.:
# pandas/2.2.5; polars/0.20.x; sqlalchemy/2.0.x. These tags are only included
# when using relevant API methods.
Expand Down
2 changes: 2 additions & 0 deletions clickhouse_connect/datatypes/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,8 @@ def data_size(self, sample: Collection) -> int:
def _data_size(self, sample: Collection) -> int:
if self.byte_size:
return self.byte_size
if len(sample) == 0:
return 1
total = 0
for x in sample:
total += len(str(x))
Expand Down
Loading
Loading