Skip to content

Commit 7c9ab6b

Browse files
authored
fix: support clickhouse in vannatoolset (#543)
* fix: support clickhouse in vannatoolset * fix: chinese
1 parent 68677b7 commit 7c9ab6b

3 files changed

Lines changed: 162 additions & 3 deletions

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: 49 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,14 +63,13 @@ 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

69-
# 验证连接字符串格式
7070
if not self.connection_string:
7171
raise ValueError("Connection string cannot be empty")
7272

73-
# 检查连接字符串格式
7473
if self.connection_string.startswith("sqlite://"):
7574
if len(self.connection_string) <= len("sqlite://"):
7675
raise ValueError(
@@ -128,9 +127,56 @@ def _post_init(self):
128127
)
129128
except (IndexError, ValueError) as e:
130129
raise ValueError(f"Invalid MySQL connection string format: {e}") from e
130+
elif self.connection_string.startswith("clickhouse://"):
131+
try:
132+
from urllib.parse import urlparse, parse_qs
133+
134+
parsed = urlparse(self.connection_string)
135+
136+
user = parsed.username
137+
password = parsed.password
138+
host = parsed.hostname
139+
port = parsed.port or 8123
140+
database = parsed.path.lstrip("/")
141+
142+
query_params = parse_qs(parsed.query)
143+
kwargs = {}
144+
145+
for key, values in query_params.items():
146+
if not values:
147+
continue
148+
149+
value = values[0]
150+
151+
if value.lower() in ("true", "false", "1", "0", "yes", "no"):
152+
kwargs[key] = value.lower() in ("true", "1", "yes")
153+
elif value.isdigit():
154+
kwargs[key] = int(value)
155+
elif value.replace(".", "", 1).isdigit():
156+
kwargs[key] = float(value)
157+
else:
158+
kwargs[key] = value
159+
160+
if not all([user, password, host, database]):
161+
raise ValueError(
162+
"Missing required connection parameters (user, password, host, database)"
163+
)
164+
165+
self.runner = ClickHouseRunner(
166+
host=host,
167+
database=database,
168+
user=user,
169+
password=password,
170+
port=port,
171+
**kwargs,
172+
)
173+
except (IndexError, ValueError, AttributeError) as e:
174+
raise ValueError(
175+
f"Invalid ClickHouse connection string format: {e}"
176+
) from e
131177
else:
132178
raise ValueError(
133-
"Unsupported connection string format. Please use sqlite://, postgresql://, or mysql://"
179+
"Unsupported connection string format. Please use sqlite://, postgresql://, mysql://, or clickhouse://"
134180
)
135181

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

0 commit comments

Comments
 (0)