Skip to content

Commit 94213a4

Browse files
committed
[WIP] feat(clickhouse): Use ClickHouse query with parameters via HTTP interface
1 parent 8e815c2 commit 94213a4

File tree

2 files changed

+41
-3
lines changed

2 files changed

+41
-3
lines changed

packages/cubejs-clickhouse-driver/src/ClickHouseDriver.ts

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -222,12 +222,35 @@ export class ClickHouseDriver extends BaseDriver implements DriverInterface {
222222
true;
223223
}
224224

225-
public async query(query: string, values: unknown[]) {
225+
public async query(query: string, values?: unknown[]) {
226226
return this.queryResponse(query, values).then((res: any) => this.normaliseResponse(res));
227227
}
228228

229-
protected queryResponse(query: string, values: unknown[]) {
230-
const formattedQuery = sqlstring.format(query, values);
229+
protected queryResponse(query: string, values?: unknown[]) {
230+
// const formattedQuery = sqlstring.format(query, values);
231+
const formattedQuery = query;
232+
233+
// `queryOptions` object will be merged into query params via querystring.stringify
234+
// https://github.com/cube-js/apla-node-clickhouse/blob/5a6577fc97ba6911171753fc65b2cd2f6170f2f7/src/clickhouse.js#L347-L348
235+
// https://github.com/cube-js/apla-node-clickhouse/blob/5a6577fc97ba6911171753fc65b2cd2f6170f2f7/src/clickhouse.js#L265-L266
236+
// https://github.com/cube-js/apla-node-clickhouse/blob/5a6577fc97ba6911171753fc65b2cd2f6170f2f7/src/clickhouse.js#L336-L338
237+
// https://github.com/cube-js/apla-node-clickhouse/blob/5a6577fc97ba6911171753fc65b2cd2f6170f2f7/src/clickhouse.js#L173-L175
238+
239+
// We can use `toSearchParams` or `formatQueryParams` from `@clickhouse/client-common` to prepare params, and extract only interesting ones
240+
// Beware - these functions marked as "For implementations usage only - should not be re-exported", so, probably, ot could be moved or disappear completely
241+
// https://github.com/ClickHouse/clickhouse-js/blob/a15cce93545c792852e34c05ce31954c75d11486/packages/client-common/src/utils/url.ts#L57-L61
242+
243+
// HTTP interface itself is documented, so it should be fine
244+
// https://clickhouse.com/docs/en/interfaces/cli#cli-queries-with-parameters
245+
// https://clickhouse.com/docs/en/interfaces/http#cli-queries-with-parameters
246+
247+
const paramsValues = Object.fromEntries((values ?? []).map((value, idx) => {
248+
const paramName = this.paramName(idx);
249+
const paramKey = `param_${paramName}`;
250+
// TODO prepare value
251+
const preparedValue = value;
252+
return [paramKey, preparedValue];
253+
}));
231254

232255
return this.withConnection((connection, queryId) => connection.querying(formattedQuery, {
233256
dataObjects: true,
@@ -241,6 +264,9 @@ export class ClickHouseDriver extends BaseDriver implements DriverInterface {
241264
//
242265
//
243266
...(this.readOnlyMode ? {} : { join_use_nulls: 1 }),
267+
268+
// Add parameter values to query string
269+
...paramsValues,
244270
}
245271
}));
246272
}
@@ -309,6 +335,15 @@ export class ClickHouseDriver extends BaseDriver implements DriverInterface {
309335
return [{ schema_name: this.config.queryOptions.database }];
310336
}
311337

338+
protected paramName(paramIndex: number): string {
339+
return `p${paramIndex}`;
340+
}
341+
342+
public param(paramIndex: number): string {
343+
// TODO not always string
344+
return `{${this.paramName(paramIndex)}:String}`;
345+
}
346+
312347
public async stream(
313348
query: string,
314349
values: unknown[],

packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,9 @@ export class ClickHouseQuery extends BaseQuery {
268268

269269
public sqlTemplates() {
270270
const templates = super.sqlTemplates();
271+
// TODO not every param is a string
272+
// TODO override timeStampParam
273+
templates.params.param = '{p{{ param_index }}:String}';
271274
templates.functions.DATETRUNC = 'DATE_TRUNC({{ args_concat }})';
272275
// TODO: Introduce additional filter in jinja? or parseDateTimeBestEffort?
273276
// https://github.com/ClickHouse/ClickHouse/issues/19351

0 commit comments

Comments
 (0)