chore: auto-commit (43 archivos)
- .mcp.json - bash/functions/infra/write_mcp_jupyter_config.md - bash/functions/infra/write_mcp_jupyter_config.sh - cpp/CMakeLists.txt - cpp/apps/chart_demo - cpp/apps/shaders_lab - cpp/functions/gfx/gl_framebuffer.cpp - cpp/functions/gfx/gl_framebuffer.h - cpp/functions/gfx/gl_framebuffer.md - cpp/functions/gfx/mesh_gpu.md - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""Query ClickHouse via the HTTP interface (port 8123) and return rows as dicts."""
|
||||
|
||||
import json
|
||||
import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
|
||||
def clickhouse_query(
|
||||
base_url: str,
|
||||
sql: str,
|
||||
*,
|
||||
user: str = "default",
|
||||
password: str = "",
|
||||
database: str = "analytics",
|
||||
timeout: float = 30.0,
|
||||
) -> list[dict]:
|
||||
"""Execute a SQL statement against ClickHouse via HTTP and return results.
|
||||
|
||||
Args:
|
||||
base_url: ClickHouse HTTP base URL without trailing slash,
|
||||
e.g. "http://127.0.0.1:18123".
|
||||
sql: Full SQL statement. For SELECT queries the server returns
|
||||
JSONEachRow automatically via the default_format param.
|
||||
For DDL/DML with no result set (CREATE, INSERT, etc.)
|
||||
the response body is empty and [] is returned.
|
||||
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:
|
||||
List of dicts, one per result row. Empty list for statements that
|
||||
produce no result set. Numbers may come back as strings for some
|
||||
ClickHouse types (e.g. Int64 is returned as a JSON string in
|
||||
JSONEachRow — cast explicitly if needed: int(row["c"])).
|
||||
|
||||
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).
|
||||
"""
|
||||
params = urllib.parse.urlencode(
|
||||
{"database": database, "default_format": "JSONEachRow"}
|
||||
)
|
||||
url = f"{base_url}/?{params}"
|
||||
|
||||
body_bytes = sql.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 query failed: HTTP {resp.status} — {body_preview}"
|
||||
)
|
||||
raw = resp.read().decode("utf-8", errors="replace")
|
||||
except urllib.error.HTTPError as exc:
|
||||
body_preview = exc.read(500).decode("utf-8", errors="replace")
|
||||
raise ValueError(
|
||||
f"ClickHouse query failed: HTTP {exc.code} — {body_preview}"
|
||||
) from exc
|
||||
|
||||
rows = []
|
||||
for line in raw.splitlines():
|
||||
line = line.strip()
|
||||
if line:
|
||||
rows.append(json.loads(line))
|
||||
return rows
|
||||
Reference in New Issue
Block a user