Source code for httk.store.backend.clickhouse.engine
"""ClickHouse database construction for :class:`~httk.store.backend.sql.engine.Backend`."""
import importlib
from typing import TYPE_CHECKING
import sqlalchemy
if TYPE_CHECKING:
from httk.store.backend.sql.engine import Backend
[docs]
def database(
cls: "type[Backend]",
url: str | sqlalchemy.URL,
*,
database: str | None = None,
) -> "Backend":
"""Build a ClickHouse-backed backend from a ``clickhousedb://`` URL.
The URL uses the SQLAlchemy ``clickhouse-connect`` dialect, for example
``clickhousedb://default:@host:8123/my_database``. ``database`` replaces the
URL path when supplied. The constructor always merges ``join_use_nulls=1``
into the URL query and selects the ``bulk-fenced`` storage profile before any
:class:`~httk.store.backend.sql.store.SqlStore` initialization occurs.
:param cls: The backend class to instantiate.
:param url: ClickHouse SQLAlchemy URL or URL string.
:param database: The database name overriding the URL path, if supplied.
:return: Connected ClickHouse backend wrapper using the bulk-fenced profile.
:raises ImportError: If ``clickhouse-connect`` is not installed; install the
``httk-store[clickhouse]`` extra.
:raises RuntimeError: If Keeper is unavailable, the server is too old, or
``join_use_nulls`` cannot be enforced.
"""
try:
importlib.import_module("clickhouse_connect")
# Importing this module registers the clickhousedb SQLAlchemy URL.
importlib.import_module("clickhouse_connect.cc_sqlalchemy")
except ImportError as error:
raise ImportError(
"the ClickHouse backend needs clickhouse-connect; install the 'httk-store[clickhouse]' extra "
"to use Backend.clickhouse()"
) from error
from sqlalchemy.engine import make_url
clickhouse_url = make_url(url) if isinstance(url, str) else url
if clickhouse_url.drivername.split("+")[0] != "clickhousedb":
raise ValueError("Backend.clickhouse() requires a clickhousedb:// SQLAlchemy URL")
if database is not None:
clickhouse_url = clickhouse_url.set(database=database)
clickhouse_url = clickhouse_url.update_query_dict({"join_use_nulls": "1"})
engine = sqlalchemy.create_engine(clickhouse_url)
try:
result = cls(engine, write_profile="bulk-fenced")
from httk.store.backend.clickhouse.support import ensure_bootstrap_table
ensure_bootstrap_table(result.engine)
return result
except BaseException:
engine.dispose()
raise