Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions packages/cubejs-clickhouse-driver/src/ClickHouseDriver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export class ClickHouseDriver extends BaseDriver implements DriverInterface {
}

protected queryResponse(query: string, values: unknown[]): Promise<ResponseJSON<Record<string, unknown>>> {
const formattedQuery = sqlstring.format(query, values);
const formattedQuery = query.replace(/___ClickHouseParam_(\d+)___/g, (_, idx) => sqlstring.escape(values[idx]));

return this.withCancel(async (connection, queryId, signal) => {
try {
Expand Down Expand Up @@ -306,6 +306,10 @@ export class ClickHouseDriver extends BaseDriver implements DriverInterface {
await this.client.close();
}

public param(paramIndex: number): string {
return `___ClickHouseParam_${paramIndex}___`;
}

public informationSchemaQuery() {
return `
SELECT name as column_name,
Expand Down Expand Up @@ -362,7 +366,7 @@ export class ClickHouseDriver extends BaseDriver implements DriverInterface {
const queryId = uuidv4();

try {
const formattedQuery = sqlstring.format(query, values);
const formattedQuery = query.replace(/___ClickHouseParam_(\d+)___/g, (_, idx) => sqlstring.escape(values[idx]));

const format = 'JSONCompactEachRowWithNamesAndTypes';

Expand Down Expand Up @@ -486,7 +490,7 @@ export class ClickHouseDriver extends BaseDriver implements DriverInterface {
}

public getTablesQuery(schemaName: string): Promise<TableQueryResult[]> {
return this.query('SELECT name as table_name FROM system.tables WHERE database = ?', [schemaName]);
return this.query('SELECT name as table_name FROM system.tables WHERE database = ___ClickHouseParam_0___', [schemaName]);
}

public override async dropTable(tableName: string, _options?: QueryOptions): Promise<void> {
Expand Down Expand Up @@ -598,7 +602,7 @@ export class ClickHouseDriver extends BaseDriver implements DriverInterface {
const { bucketName, path } = this.parseBucketUrl(this.config.exportBucket.bucketName);
const exportPrefix = path ? `${path}/${uuidv4()}` : uuidv4();

const formattedQuery = sqlstring.format(`
const formattedQuery = `
INSERT INTO FUNCTION
s3(
'https://${bucketName}.s3.${this.config.exportBucket.region}.amazonaws.com/${exportPrefix}/export.csv.gz',
Expand All @@ -607,7 +611,7 @@ export class ClickHouseDriver extends BaseDriver implements DriverInterface {
'CSV'
)
${sql}
`, params);
`.replace(/___ClickHouseParam_(\d+)___/g, (_, idx) => sqlstring.escape(params[idx]));

await this.command(formattedQuery);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ describe('ClickHouseDriver', () => {
await driver.createSchemaIfNotExists(name);
await driver.command(`CREATE TABLE ${name}.test (x Int32, s String) ENGINE Log`);
await driver.insert(`${name}.test`, [[1, 'str1'], [2, 'str2'], [3, 'str3']]);
const values = await driver.query(`SELECT * FROM ${name}.test WHERE x = ?`, [2]);
const values = await driver.query(`SELECT * FROM ${name}.test WHERE x = ___ClickHouseParam_0___`, [2]);
expect(values).toEqual([{ x: '2', s: 'str2' }]);
} finally {
await driver.command(`DROP DATABASE ${name}`);
Expand Down Expand Up @@ -249,7 +249,7 @@ describe('ClickHouseDriver', () => {

it('datetime with specific timezone', async () => {
await doWithDriver(async (driver) => {
const rows = await driver.query('SELECT toDateTime(?, \'Asia/Istanbul\') as dt', [
const rows = await driver.query('SELECT toDateTime(___ClickHouseParam_0___, \'Asia/Istanbul\') as dt', [
'2020-01-01 00:00:00'
]);
expect(rows).toEqual([{
Expand Down
11 changes: 11 additions & 0 deletions packages/cubejs-schema-compiler/src/adapter/ClickHouseQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { BaseQuery } from './BaseQuery';
import { BaseFilter } from './BaseFilter';
import { UserError } from '../compiler/UserError';
import { BaseTimeDimension } from './BaseTimeDimension';
import { ParamAllocator } from './ParamAllocator';

const GRANULARITY_TO_INTERVAL = {
day: 'Day',
Expand Down Expand Up @@ -30,7 +31,17 @@ class ClickHouseFilter extends BaseFilter {
}
}

class ClickHouseParamAllocator extends ParamAllocator {
public paramPlaceHolder(paramIndex) {
return `___ClickHouseParam_${paramIndex}___`;
}
}

export class ClickHouseQuery extends BaseQuery {
public newParamAllocator(expressionParams) {
return new ClickHouseParamAllocator(expressionParams);
}

public newFilter(filter) {
return new ClickHouseFilter(this, filter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createClient } from '@clickhouse/client';
import type { ClickHouseClient, ResponseJSON } from '@clickhouse/client';
import { GenericContainer } from 'testcontainers';
import type { StartedTestContainer } from 'testcontainers';
import { format as formatSql } from 'sqlstring';
import { escape } from 'sqlstring';
import { v4 as uuidv4 } from 'uuid';
import moment from 'moment';

Expand Down Expand Up @@ -158,7 +158,7 @@ export class ClickHouseDbRunner extends BaseDbRunner {
const requests = queries
.map(async ([query, params]) => {
const resultSet = await clickHouse.query({
query: formatSql(query, params),
query: query.replace(/___ClickHouseParam_(\d+)___/g, (_, idx) => escape(params[idx])),
format: 'JSON',
clickhouse_settings: {
join_use_nulls: 1,
Expand Down