"""Insert rows into ClickHouse via the HTTP interface (port 8123).""" import json import urllib.error import urllib.parse import urllib.request def clickhouse_insert_rows( base_url: str, table: str, rows: list[dict], *, user: str = "default", password: str = "", database: str = "analytics", timeout: float = 30.0, ) -> int: """Insert a list of dicts into a ClickHouse table using JSONEachRow format. Args: base_url: ClickHouse HTTP base URL without trailing slash, e.g. "http://127.0.0.1:18123". table: Fully-qualified or bare table name, e.g. "analytics.gnula_movies". rows: List of dicts to insert. Each dict becomes one JSON line. user: ClickHouse username (default "default"). password: ClickHouse password (default empty string). database: Target database sent as query param (default "analytics"). timeout: Socket timeout in seconds (default 30.0). Returns: Number of rows inserted (len(rows)). Returns 0 if rows is empty without contacting the server. Raises: ValueError: On non-200 HTTP response, with status code and first 500 chars of the response body. urllib.error.URLError: On network-level errors (connection refused, DNS failure, timeout). """ if not rows: return 0 query = f"INSERT INTO {table} FORMAT JSONEachRow" params = urllib.parse.urlencode({"database": database, "query": query}) url = f"{base_url}/?{params}" body = "\n".join(json.dumps(row, ensure_ascii=False) for row in rows) body_bytes = body.encode("utf-8") req = urllib.request.Request( url, data=body_bytes, method="POST", headers={ "Content-Type": "text/plain", "X-ClickHouse-User": user, "X-ClickHouse-Key": password, }, ) try: with urllib.request.urlopen(req, timeout=timeout) as resp: if resp.status != 200: body_preview = resp.read(500).decode("utf-8", errors="replace") raise ValueError( f"ClickHouse insert failed: HTTP {resp.status} — {body_preview}" ) return len(rows) except urllib.error.HTTPError as exc: body_preview = exc.read(500).decode("utf-8", errors="replace") raise ValueError( f"ClickHouse insert failed: HTTP {exc.code} — {body_preview}" ) from exc