Skip to content

About

A ClickHouse backend implementation that brings ClickHouse's high-performance analytical processing to the ActiveRecord pattern while maintaining its elegant Pythonic interface.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

rhosocial-activerecord-clickhouse ($\rho_{\mathbf{AR}\text{-clickhouse}}$)

PyPI version Python Tests Apache 2.0 License Powered by vistart

rhosocial ActiveRecord Logo

ClickHouse Backend for rhosocial-activerecord

Columnar Types · Capability-Gated Dialect · Snowflake IDs · Sync

A ClickHouse backend implementation for rhosocial-activerecord — the ActiveRecord pattern with type safety and rich features, adapted to ClickHouse's columnar, OLTP-deviant world view.

Why This Backend?

1. Honest Semantics, Not Emulation

ClickHouse is not a row-store OLTP engine, and this backend does not pretend it is. Relational guarantees that ClickHouse does not provide are declared through capability switches and fail fast with UnsupportedFeatureError instead of being silently emulated:

Feature Behaviour in this backend
ACID transactions transaction() degrades to a no-op context manager; rollback semantics raise
AUTO_INCREMENT Client-side snowflake Int64 ids generated by the backend
FOREIGN KEY / UNIQUE Not emitted; DDL stays ClickHouse-native (ENGINE, ORDER BY, skip indexes)
UPSERT / ON CONFLICT / REPLACE Raises UnsupportedFeatureError
JSON access Native functions: JSONExtractString / JSONExtractRaw / JSON_VALUE
UPDATE / DELETE Lightweight mutations executed with mutations_sync=1; affected_rows = pre-counted matched rows

2. ClickHouse-Native Strengths

Feature This Backend Generic Solutions
Columnar types Int/UInt, Decimal, Bool, UUID, IPv4/6, Enum8/16, Array, Map, Tuple, Nullable, LowCardinality, JSON Lossy VARCHAR mapping
DDL control ENGINE, ORDER BY, PARTITION BY, TTL clauses One-size-fits-all DDL
Introspection system.* tables (settings, metrics, replicas, processes) information_schema guesses
Parameterized SQL DB-API placeholders via clickhouse-connect HTTP String concatenation

3. Built for Production

  • Connection pooling support through the shared backend pool
  • Automatic version adaptation — dialect and type adapters re-tune to the actual server version on connect
  • Error mapping from ClickHouse error codes to Python exceptions
  • Synchronous by design — clickhouse-connect is a sync-only driver; there is deliberately no half-working async layer

Requirements

  • Python >= 3.10, < 3.15 (per clickhouse-connect)
  • ClickHouse server: any release line currently under maintenance (25.8 LTS, 26.3 LTS, 26.7+ tested; older lines may work but are untested)
  • rhosocial-activerecord >= 1.0.0.dev30

Quick Start

Installation

pip install rhosocial-activerecord-clickhouse

Basic Usage

from typing import Optional, ClassVar
from pydantic import Field
from rhosocial.activerecord import ActiveRecord, FieldProxy
from rhosocial.activerecord.api.connection import ConnectionConfig
from rhosocial.activerecord.backend.impl.clickhouse import ClickHouseBackend

class User(ActiveRecord):
    __table_name__ = "users"
    __primary_key__ = "id"
    c: ClassVar[FieldProxy] = FieldProxy()

    id: Optional[int] = None          # snowflake id, assigned on save()
    username: str = Field(min_length=3, max_length=50)
    email: str
    age: Optional[int] = None         # -> Nullable(Int32) column

config = ConnectionConfig(
    host="localhost", port=8123,
    database="test_db", username="root", password="password",
)

User.configure(config, ClickHouseBackend)

# Create — id is generated client-side and returned
user = User(username="alice", email="alice@example.com")
user.save()

# Query
young = User.query().where(User.c.age < 30).order_by(User.c.username).all()

# Update
user.age = 31
user.save()   # lightweight mutation, synchronous completion

# Delete
user.delete()

Schema note: model tables should be created as MergeTree tables whose optional fields use Nullable(T); writing NULL into a non-nullable String column stores an empty string. Primary keys map to the ORDER BY sorting key, which cannot be updated.

What This Backend Does NOT Do

  • No cross-statement transactions (ClickHouse mutations are per-part atomic; the context manager exists only so generic code paths keep working)
  • No uniqueness enforcement — deduplication is your engine's job (ReplacingMergeTree, CollapsingMergeTree)
  • No async API (the driver is sync-only)
  • No SQL-standard FULLTEXT index / MATCH ... AGAINST / JSON_TABLE (raises UnsupportedFeatureError; use skip indexes and JSONExtract*)
  • No MySQL-specific feature surface: spatial types / ST_*, the 9.0 VECTOR type, SET, stored procedures / CALL, LOAD DATA / LOAD XML, admin commands (FLUSH / RESET / KILL / GRANT ...), TABLE / VALUES constructors, ANALYZE / CHECK / REPAIR TABLE maintenance, /*+ */ optimizer hints, or JSON Duality Views — all fail fast

Testing

The full shared testsuite is bridged under tests/rhosocial/activerecord_clickhouse_test/feature/ (basic, events, interface, mixins, query, relation):

export PYTHONPATH=src:tests
python -m pytest tests/rhosocial/activerecord_clickhouse_test -p no:logging -p no:cacheprovider

A local ClickHouse is required; connection defaults live in tests/config/clickhouse_scenarios.yaml (override path via CLICKHOUSE_SCENARIOS_CONFIG_PATH).

CI exercises the matrix across Python 3.10–3.14 and the maintained ClickHouse release lines (25.8 LTS, 26.3 LTS, 26.7) — see .github/workflows/test.yml.

Documentation

  • AGENTS.md — repository conventions for humans and AI agents
  • CLAUDE.md — same overview, Claude Code entry point
  • .claude/ — backend knowledge base and development guides

Contributing

Issues and pull requests are welcome at the issue tracker. Please follow the core project's coding and changelog standards.

License

Apache License 2.0

About

A ClickHouse backend implementation that brings ClickHouse's high-performance analytical processing to the ActiveRecord pattern while maintaining its elegant Pythonic interface.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages