Skip to content

Commit 9b6b27a

Browse files
authoredNov 24, 2016
Merge pull request #1409 from denisov-vlad/clickhouse
[Datasources] Add: Add query runner for Yandex ClickHouse
2 parents b9aeb2f + f572315 commit 9b6b27a

File tree

2 files changed

+111
-0
lines changed

2 files changed

+111
-0
lines changed
 

‎redash/query_runner/clickhouse.py

+110
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import json
2+
import logging
3+
from redash.query_runner import *
4+
from redash.utils import JSONEncoder
5+
import requests
6+
logger = logging.getLogger(__name__)
7+
8+
9+
class ClickHouse(BaseSQLQueryRunner):
10+
noop_query = "SELECT 1"
11+
12+
@classmethod
13+
def configuration_schema(cls):
14+
return {
15+
"type": "object",
16+
"properties": {
17+
"url": {
18+
"type": "string",
19+
"default": "http://127.0.0.1:8123"
20+
},
21+
"user": {
22+
"type": "string",
23+
"default": "default"
24+
},
25+
"password": {
26+
"type": "string"
27+
},
28+
"dbname": {
29+
"type": "string",
30+
"title": "Database Name"
31+
}
32+
},
33+
"required": ["dbname"],
34+
"secret": ["password"]
35+
}
36+
37+
@classmethod
38+
def type(cls):
39+
return "clickhouse"
40+
41+
def __init__(self, configuration):
42+
super(ClickHouse, self).__init__(configuration)
43+
44+
def _get_tables(self, schema):
45+
query = "SELECT database, table, name FROM system.columns WHERE database NOT IN ('system')"
46+
47+
results, error = self.run_query(query, None)
48+
49+
if error is not None:
50+
raise Exception("Failed getting schema.")
51+
52+
results = json.loads(results)
53+
54+
for row in results['rows']:
55+
table_name = '{}.{}'.format(row['database'], row['table'])
56+
57+
if table_name not in schema:
58+
schema[table_name] = {'name': table_name, 'columns': []}
59+
60+
schema[table_name]['columns'].append(row['name'])
61+
62+
return schema.values()
63+
64+
def _send_query(self, data, stream=False):
65+
r = requests.post(self.configuration['url'], data=data, stream=stream, params={
66+
'user': self.configuration['user'], 'password': self.configuration['password'],
67+
'database': self.configuration['dbname']
68+
})
69+
if r.status_code != 200:
70+
raise Exception(r.text)
71+
return r.json()
72+
73+
@staticmethod
74+
def _define_column_type(column):
75+
c = column.lower()
76+
if 'int' in c:
77+
return TYPE_INTEGER
78+
elif 'float' in c:
79+
return TYPE_FLOAT
80+
elif 'datetime' == c:
81+
return TYPE_DATETIME
82+
elif 'date' == c:
83+
return TYPE_DATE
84+
else:
85+
return TYPE_STRING
86+
87+
def _clickhouse_query(self, query):
88+
query += ' FORMAT JSON'
89+
result = self._send_query(query)
90+
columns = [{'name': r['name'], 'friendly_name': r['name'],
91+
'type': self._define_column_type(r['type'])} for r in result['meta']]
92+
return {'columns': columns, 'rows': result['data']}
93+
94+
def run_query(self, query, user):
95+
logger.debug("Clickhouse is about to execute query: %s", query)
96+
if query == "":
97+
json_data = None
98+
error = "Query is empty"
99+
return json_data, error
100+
try:
101+
q = self._clickhouse_query(query)
102+
data = json.dumps(q, cls=JSONEncoder)
103+
error = None
104+
except Exception as e:
105+
data = None
106+
logging.exception(e)
107+
error = unicode(e)
108+
return data, error
109+
110+
register(ClickHouse)

‎redash/settings.py

+1
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,7 @@ def all_settings():
174174
'redash.query_runner.hive_ds',
175175
'redash.query_runner.impala_ds',
176176
'redash.query_runner.vertica',
177+
'redash.query_runner.clickhouse',
177178
'redash.query_runner.treasuredata',
178179
'redash.query_runner.sqlite',
179180
'redash.query_runner.dynamodb_sql',

0 commit comments

Comments
 (0)
Please sign in to comment.