Skip to content

Commit 12cccd3

Browse files
committed
fix: support clickhouse in vannatoolset
1 parent 68677b7 commit 12cccd3

3 files changed

Lines changed: 171 additions & 1 deletion

File tree

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from .sql_runner import ClickHouseRunner
16+
17+
__all__ = ["ClickHouseRunner"]
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
# Copyright (c) 2025 Beijing Volcano Engine Technology Co., Ltd. and/or its affiliates.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
import pandas as pd
16+
17+
from vanna.capabilities.sql_runner import SqlRunner, RunSqlToolArgs
18+
from vanna.core.tool import ToolContext
19+
20+
21+
class ClickHouseRunner(SqlRunner):
22+
"""ClickHouse implementation of the SqlRunner interface."""
23+
24+
def __init__(
25+
self,
26+
host: str,
27+
database: str,
28+
user: str,
29+
password: str,
30+
port: int = 9000,
31+
**kwargs,
32+
):
33+
"""Initialize with ClickHouse connection parameters.
34+
35+
Args:
36+
host: Database host address
37+
database: Database name
38+
user: Database user
39+
password: Database password
40+
port: Database port (default: 9000 for native protocol)
41+
**kwargs: Additional clickhouse_driver connection parameters
42+
"""
43+
try:
44+
from clickhouse_driver import Client
45+
46+
self.Client = Client
47+
except ImportError as e:
48+
raise ImportError(
49+
"clickhouse-driver package is required. "
50+
"Install with: pip install clickhouse-driver"
51+
) from e
52+
53+
self.host = host
54+
self.port = port
55+
self.user = user
56+
self.password = password
57+
self.database = database
58+
self.kwargs = kwargs
59+
60+
async def run_sql(self, args: RunSqlToolArgs, context: ToolContext) -> pd.DataFrame:
61+
"""Execute SQL query against ClickHouse database and return results as DataFrame.
62+
63+
Args:
64+
args: SQL query arguments
65+
context: Tool execution context
66+
67+
Returns:
68+
DataFrame with query results
69+
70+
Raises:
71+
Exception: If query execution fails
72+
"""
73+
# Connect to the database
74+
client = self.Client(
75+
host=self.host,
76+
port=self.port,
77+
user=self.user,
78+
password=self.password,
79+
database=self.database,
80+
**self.kwargs,
81+
)
82+
83+
try:
84+
# Execute the query
85+
result = client.execute(args.sql, with_column_types=True)
86+
87+
# result is a tuple: (data, [(column_name, column_type), ...])
88+
data = result[0]
89+
columns = [col[0] for col in result[1]]
90+
91+
# Create a pandas dataframe from the results
92+
df = pd.DataFrame(data, columns=columns)
93+
return df
94+
95+
finally:
96+
client.disconnect()

veadk/tools/vanna_tools/vanna_toolset.py

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ def _post_init(self):
6363
from vanna.integrations.sqlite import SqliteRunner
6464
from vanna.integrations.postgres import PostgresRunner
6565
from vanna.integrations.mysql import MySQLRunner
66+
from .clickhouse.sql_runner import ClickHouseRunner
6667
from vanna.tools import LocalFileSystem
6768
from vanna.integrations.local.agent_memory import DemoAgentMemory
6869

@@ -128,9 +129,65 @@ def _post_init(self):
128129
)
129130
except (IndexError, ValueError) as e:
130131
raise ValueError(f"Invalid MySQL connection string format: {e}") from e
132+
elif self.connection_string.startswith("clickhouse://"):
133+
try:
134+
from urllib.parse import urlparse, parse_qs
135+
136+
# 解析 URI
137+
parsed = urlparse(self.connection_string)
138+
139+
# 提取基本信息
140+
user = parsed.username
141+
password = parsed.password
142+
host = parsed.hostname
143+
port = parsed.port or 8123 # 默认端口
144+
database = parsed.path.lstrip("/")
145+
146+
# 解析查询参数
147+
query_params = parse_qs(parsed.query)
148+
kwargs = {}
149+
150+
# 处理所有查询参数
151+
for key, values in query_params.items():
152+
if not values:
153+
continue
154+
155+
value = values[0] # 取第一个值
156+
157+
# 处理布尔值参数
158+
if value.lower() in ("true", "false", "1", "0", "yes", "no"):
159+
kwargs[key] = value.lower() in ("true", "1", "yes")
160+
# 处理数字参数
161+
elif value.isdigit():
162+
kwargs[key] = int(value)
163+
# 处理浮点数参数
164+
elif value.replace(".", "", 1).isdigit():
165+
kwargs[key] = float(value)
166+
# 其他参数保持字符串
167+
else:
168+
kwargs[key] = value
169+
170+
# 验证必需参数
171+
if not all([user, password, host, database]):
172+
raise ValueError(
173+
"Missing required connection parameters (user, password, host, database)"
174+
)
175+
176+
self.runner = ClickHouseRunner(
177+
host=host,
178+
database=database,
179+
user=user,
180+
password=password,
181+
port=port,
182+
**kwargs,
183+
)
184+
except (IndexError, ValueError, AttributeError) as e:
185+
raise ValueError(
186+
f"Invalid ClickHouse connection string format: {e}"
187+
) from e
131188
else:
132189
raise ValueError(
133-
"Unsupported connection string format. Please use sqlite://, postgresql://, or mysql://"
190+
"Unsupported connection string format. Please use sqlite://, postgresql://, mysql://, or clickhouse://"
134191
)
135192

136193
if not os.path.exists(self.file_storage):

0 commit comments

Comments
 (0)