Skip to content
This repository was archived by the owner on Jun 4, 2024. It is now read-only.

Commit b46c2bc

Browse files
Sean Rabautbriandennis
Sean Rabaut
authored andcommitted
ClickHouse integration
1 parent c10c1a6 commit b46c2bc

File tree

7 files changed

+1433
-3181
lines changed

7 files changed

+1433
-3181
lines changed

app/components/Settings/Tabs/Tab.react.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ export default class ConnectionTab extends Component {
4444
label = connectionObject.storage;
4545
} else if (connectionObject.dialect === DIALECTS.BIGQUERY) {
4646
label = `Big Query ${connectionObject.database}`;
47+
} else if(connectionObject.dialect === DIALECTS.CLICKHOUSE) {
48+
label = `ClickHouse ${connectionObject.database}`;
4749
} else if (connectionObject.dialect === DIALECTS.DATA_WORLD) {
4850
const pathNames = getPathNames(connectionObject.url);
4951
if (pathNames.length >= 3) {

app/constants/constants.js

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ export const DIALECTS = {
1919
DATA_WORLD: 'data.world',
2020
ATHENA: 'athena',
2121
CSV: 'csv',
22-
BIGQUERY: 'bigquery'
22+
BIGQUERY: 'bigquery',
23+
CLICKHOUSE: 'clickhouse'
2324
};
2425

2526
export const SQL_DIALECTS_USING_EDITOR = [
@@ -36,7 +37,8 @@ export const SQL_DIALECTS_USING_EDITOR = [
3637
'data.world',
3738
'athena',
3839
'csv',
39-
'bigquery'
40+
'bigquery',
41+
'clickhouse'
4042
];
4143

4244
const commonSqlOptions = [
@@ -270,6 +272,22 @@ export const CONNECTION_CONFIG = {
270272
'type': 'filedrop',
271273
'description': 'The location of the Google Service Account Key File'
272274
}
275+
],
276+
[DIALECTS.CLICKHOUSE]: [
277+
{'label': 'Username', 'value': 'username', 'type': 'text'},
278+
{'label': 'Password', 'value': 'password', 'type': 'password'},
279+
{
280+
'label': 'Host',
281+
'value': 'host',
282+
'type': 'text'
283+
},
284+
{
285+
'label': 'Port',
286+
'value': 'port',
287+
'type': 'number',
288+
'description': 'Server port number (e.g. 8123)'
289+
},
290+
{'label': 'Database', 'value': 'database', 'type': 'text'},
273291
]
274292
};
275293

@@ -291,7 +309,8 @@ export const LOGOS = {
291309
[DIALECTS.APACHE_DRILL]: 'images/apache_drill-logo.png',
292310
[DIALECTS.DATA_WORLD]: 'images/dataworld-logo.png',
293311
[DIALECTS.ATHENA]: 'images/athena-logo.png',
294-
[DIALECTS.BIGQUERY]: 'images/bigquery-logo.png'
312+
[DIALECTS.BIGQUERY]: 'images/bigquery-logo.png',
313+
[DIALECTS.CLICKHOUSE]: 'images/clickhouse-logo.png'
295314
};
296315

297316
export function PREVIEW_QUERY(connection, table, elasticsearchIndex) {
@@ -311,6 +330,7 @@ export function PREVIEW_QUERY(connection, table, elasticsearchIndex) {
311330
case DIALECTS.DATA_WORLD:
312331
case DIALECTS.REDSHIFT:
313332
case DIALECTS.ATHENA:
333+
case DIALECTS.CLICKHOUSE:
314334
return `SELECT * FROM ${table} LIMIT 1000`;
315335
case DIALECTS.MSSQL:
316336
return (connection.database) ?
@@ -485,6 +505,12 @@ export const SAMPLE_DBS = {
485505
projectId: 'Plotly',
486506
database: 'plotly',
487507
keyFilename: '/home/plotly/falcon/google-credentials.json'
508+
},
509+
[DIALECTS.CLICKHOUSE]: {
510+
username: 'default',
511+
password: 'connecttoplotly',
512+
host: 'clickhouse.test.plotly.host',
513+
port: 8123,
488514
}
489515
};
490516

app/images/clickhouse-logo.png

377 Bytes
Loading

backend/persistent/datastores/Datastores.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import * as DataWorld from './dataworld';
1515
import * as DatastoreMock from './datastoremock';
1616
import * as Athena from './athena';
1717
import * as BigQuery from './bigquery';
18+
import * as ClickHouse from './clickhouse';
1819

1920
const CSV = require('./csv');
2021
const Oracle = require('./oracle.js');
@@ -68,6 +69,8 @@ function getDatastoreClient(connection) {
6869
return Oracle;
6970
} else if (dialect === 'bigquery') {
7071
return BigQuery;
72+
} else if (dialect === 'clickhouse') {
73+
return ClickHouse;
7174
}
7275
return Sql;
7376
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import ClickHouse from '@apla/clickhouse';
2+
3+
/**
4+
* The following function will create a ClickHouse client
5+
* @param {String} host
6+
* @param {Number} port
7+
* @param {String} username
8+
* @param {String} password
9+
* @param {String} database
10+
* @returns {Object} ClickHouse client
11+
*/
12+
function createClient({ host, port, username = '', password = '', database }) {
13+
return new ClickHouse({
14+
host,
15+
port,
16+
auth: `${username}:${password}`,
17+
queryOptions: {
18+
database
19+
}
20+
});
21+
}
22+
23+
/**
24+
* The following function will connect to the datasource
25+
* @param {Object} connection
26+
* @returns {Promise} that resolves when the connection succeeds
27+
*/
28+
export function connect(connection) {
29+
return createClient(connection).pinging()
30+
}
31+
32+
/**
33+
* The following function will return a list of schemas.
34+
* @param {Object} connection
35+
* @returns {Promise} that resolves to {
36+
* columnnames: [ 'tablename', 'column_name','data_type' ],
37+
* rows: [[tablename1, columnname1, datatype1], ...]] }
38+
*/
39+
export function schemas(connection) {
40+
return query(`
41+
SELECT table, name, type
42+
FROM system.columns
43+
WHERE database = '${connection.database}'
44+
ORDER BY table
45+
`, connection).then(({ rows }) => ({
46+
columnnames: [ 'tablename', 'column_name','data_type' ],
47+
rows
48+
}))
49+
}
50+
51+
52+
/**
53+
* The following function will return a list of tables.
54+
* @param {Object} connection
55+
* @returns {Promise} that resolves to an array of table names
56+
*/
57+
export function tables(connection) {
58+
return query('SHOW TABLES', connection).then(({ rows }) => rows.map(row => row[0]))
59+
}
60+
61+
/**
62+
* The following function will execute a query against the
63+
* data source. The function should return a tuple which contains
64+
* two elements. The first is an array of strings which is the column names
65+
* The second is a two dimensional array which represents the rows to be displayed
66+
* @param {String|Object} queryObject
67+
* @param {Object} connection
68+
* @returns {Promise} that resolves to { columnnames, rows }
69+
*/
70+
export function query(queryObject, connection) {
71+
const client = createClient(connection)
72+
73+
const stream = client.query(queryObject)
74+
75+
let columnnames = []
76+
const rows = [];
77+
78+
return new Promise((resolve, reject) => {
79+
stream.on('metadata', columns => {
80+
columnnames = columns.map(({ name }) => name)
81+
})
82+
83+
stream.on('data', row => rows.push(row))
84+
85+
stream.on('error', err => reject(err))
86+
87+
stream.on('end', () => resolve({ columnnames, rows }))
88+
})
89+
}

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -241,6 +241,7 @@
241241
"yamljs": "^0.3.0"
242242
},
243243
"dependencies": {
244+
"@apla/clickhouse": "^1.5.3",
244245
"alasql": "^0.4.5",
245246
"csv-parse": "^2.0.0",
246247
"dtrace-provider": "^0.8.6",

0 commit comments

Comments
 (0)